<template>
<ArtTableFullScreen>
    <div shadow="never" class="custom-pane" :style="{ height: 'calc(100% - 12px)',overflow:'hidden'}" id="table-full-screen">
		<div class="head">
			<ElRow :gutter="20" style="width: 100%;padding-top: 0.5rem;padding-bottom: 0.5rem;">
				<ElCol :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
					<h4 class="page-title-bar">
					<span>{{ appTitle }}</span>
					<span class="page-switch-wrap">
						<i class="el-icon-document" />
						<span class="page-switch-label">页面：</span>
							<el-select
							  v-model="dynamicVisualStore.currentPageIndex"
							  class="page-switch-select"
							  @change="switchPage"
							  :teleported="false"
							  :disabled="dynamicVisualStore.pages.length === 0"
								:empty-values="validEmptyValues"
								empty-text="请创建页面"
								no-match-text="请创建页面"
								placeholder="请创建页面"
							>
								<el-option
									v-for="(page, idx) in dynamicVisualStore.pages"
									:key="page.id"
									:label="page.title"
									:value="idx"
								>
									<div style="display: flex;align-items: center;justify-content: space-between;">
										<span>{{ page.title }}</span>
										<el-button
											text
											size="small"
											style="float: right; color: #f56c6c; margin-left: 8px;"
											@click.stop="removePage(idx)"
											:icon="Delete"
										/>
									</div>
								</el-option>
							</el-select>
						</span>
					</h4>
				</ElCol>
				<ElCol :xs="24" :sm="12" :md="12" :lg="12" :xl="12" style="display: flex; justify-content: end">
							
					<div class="btn" @click="createNewPage()" title="新建页面">
					  <i class="iconfont-sys">&#xe602;</i>
					</div>
					
					<div class="btn" @click="dynamicVisualStore.pages.length === 0 ? undefined : dynamicVisualStore.debugMode=!dynamicVisualStore.debugMode " :title="dynamicVisualStore.debugMode==true?'停止调试':'开启调试'" :disabled="dynamicVisualStore.pages.length === 0" :style="(dynamicVisualStore.debugMode ? 'background-color:var(--el-color-primary);' : 'background-color:rgba(var(--art-gray-200-rgb), 0.8);') + (dynamicVisualStore.pages.length === 0 ? 'opacity: 0.5; cursor: not-allowed;' : '')">
						<i v-if="dynamicVisualStore.debugMode==true" class="iconfont" :style="dynamicVisualStore.debugMode ? 'color:#fff' : 'color:rgba(var(--art-gray-700), 0.8)'">&#xe601;</i>
						<i v-else class="iconfont" :style="dynamicVisualStore.debugMode ? 'color:#fff' : 'color:rgba(var(--art-gray-700), 0.8)'">&#xe60b;</i>
					</div>
					
					<div class="btn" @click="dynamicVisualStore.pages.length === 0 ? undefined : moveableBoxShank=!moveableBoxShank " :title="moveableBoxShank==true?'关闭拖拽手柄':'开启拖拽手柄'" :disabled="dynamicVisualStore.pages.length === 0" :style="(moveableBoxShank ? 'background-color:var(--el-color-primary);' : 'background-color:rgba(var(--art-gray-200-rgb), 0.8);') + (dynamicVisualStore.pages.length === 0 ? 'opacity: 0.5; cursor: not-allowed;' : '')">
						<i class="iconfont" :style="moveableBoxShank ? 'color:#fff' : 'color:rgba(var(--art-gray-700), 0.8)'">&#xe636;</i>
					</div>

					<div class="btn-group">
						<div class="b" @click="dynamicVisualStore.pages.length === 0 || selectedIds.length < 2 ? undefined : groupComponents(selectedIds)" :disabled="selectedIds.length < 2 || dynamicVisualStore.pages.length === 0" title="分组 (Ctrl+G)" :style="selectedIds.length < 2 || dynamicVisualStore.pages.length === 0 ? 'opacity: 0.5; cursor: not-allowed;' : ''">
							<i class="iconfont">&#xe60f;</i>
						</div>
							<div class="b" @click="dynamicVisualStore.pages.length === 0 || (!hasGroupedComponents && selectedGroupIds.length === 0) ? undefined : ungroupSelected()" :disabled="(!hasGroupedComponents && selectedGroupIds.length === 0) || dynamicVisualStore.pages.length === 0" title="取消分组 (Ctrl+Shift+G)" :style="!hasGroupedComponents && selectedGroupIds.length === 0 || dynamicVisualStore.pages.length === 0 ? 'opacity: 0.5; cursor: not-allowed;' : ''">
							<i class="iconfont">&#xe62a;</i>
						</div>
					</div>

					<div class="btn-group">
						<div class="b" @click="dynamicVisualStore.pages.length === 0 || historyIndex < 0 ? undefined : undo()" :disabled="historyIndex < 0 || dynamicVisualStore.pages.length === 0" title="撤销 (Ctrl+Z)" :style="historyIndex < 0 || dynamicVisualStore.pages.length === 0 ? 'opacity: 0.5; cursor: not-allowed;' : ''">
							<i class="iconfont-sys">&#xe65e;</i>
						</div>
							<div class="b" @click="dynamicVisualStore.pages.length === 0 || historyIndex >= history.length - 1 ? undefined : redo()" :disabled="historyIndex >= history.length - 1 || dynamicVisualStore.pages.length === 0" title="重做 (Ctrl+Y)" :style="historyIndex >= history.length - 1 || dynamicVisualStore.pages.length === 0 ? 'opacity: 0.5; cursor: not-allowed;' : ''">
							<i class="iconfont-sys">&#xe659;</i>
						</div>
					</div>

					<div class="btn"  @click="dynamicVisualStore.pages.length === 0 || selectedIds.length === 0 ? undefined : deleteSelected()" :disabled="selectedIds.length === 0 || dynamicVisualStore.pages.length === 0" title="删除 (Delete)" :style="selectedIds.length === 0 || dynamicVisualStore.pages.length === 0 ? 'opacity: 0.5; cursor: not-allowed;' : ''">
						<i class="iconfont-sys">&#xe850;</i>
					</div>

					<div class="btn" @click="dynamicVisualStore.pages.length === 0 ? undefined : toggleSnap()" title="辅助线吸附" :disabled="dynamicVisualStore.pages.length === 0" :style="(snapEnabled ? 'background-color:var(--el-color-primary);' : 'background-color:rgba(var(--art-gray-200-rgb), 0.8);') + (dynamicVisualStore.pages.length === 0 ? 'opacity: 0.5; cursor: not-allowed;' : '')">
							<i class="iconfont" :style="snapEnabled ? 'color:#fff' : 'color:rgba(var(--art-gray-700), 0.8)'">&#xe63b;</i>
					</div>

					<div class="btn" @click="dynamicVisualStore.pages.length === 0 ? undefined : saveProject()" title="保存" :disabled="dynamicVisualStore.pages.length === 0" :style="dynamicVisualStore.pages.length === 0 ? 'opacity: 0.5; cursor: not-allowed;' : ''">
						<i class="iconfont">&#xe611;</i>
					</div>

					<div class="btn" @click="dynamicVisualStore.pages.length === 0 ? undefined : preview()" title="预览" :disabled="dynamicVisualStore.pages.length === 0" :style="dynamicVisualStore.pages.length === 0 ? 'opacity: 0.5; cursor: not-allowed;' : ''">
						<i class="iconfont">&#xe612;</i>
					</div>

					<div class="btn" @click="dynamicVisualStore.pages.length === 0 ? undefined : publish()" title="发布" :disabled="dynamicVisualStore.pages.length === 0" :style="dynamicVisualStore.pages.length === 0 ? 'opacity: 0.5; cursor: not-allowed;' : ''">
						<i class="iconfont-sys">&#xe87b;</i>
					</div>

					<div class="btn" @click="dynamicVisualStore.pages.length === 0 ? undefined : exportProject()" title="导出" :disabled="dynamicVisualStore.pages.length === 0" :style="dynamicVisualStore.pages.length === 0 ? 'opacity: 0.5; cursor: not-allowed;' : ''">
						<i class="iconfont-sys">&#xe6d1;</i>
					</div>

					<!-- <div class="btn" @click="pages.length === 0 ? undefined : toggleFullScreen()" :title="(isFullScreen ? '退出全屏' : '全屏')" :disabled="pages.length === 0" :style="pages.length === 0 ? 'opacity: 0.5; cursor: not-allowed;' : ''">
								<i class="iconfont-sys">{{ isFullScreen ? '&#xe62d;' : '&#xe8ce;' }}</i>
							</div> -->
							
					<div class="btn" @click="backProject()" title="返回" >
						<i class="iconfont-sys">&#xe772;</i>
					</div>
							  
					<el-popover width="260px">
						<template #reference>
									<div class="btn"  title="帮助" :disabled="dynamicVisualStore.pages.length === 0" :style="dynamicVisualStore.pages.length === 0 ? 'opacity: 0.5; cursor: not-allowed;' : ''">
								<i class="iconfont-sys">&#xe711;</i>
							</div>
						</template>
					  <div>
						<el-text tag="b">快捷键:</el-text>
						<el-text tag="p">
							Ctrl+A(全选
						</el-text>
						<el-text tag="p">
							Ctrl+Z(撤销) | Ctrl+Y(重做)
						</el-text> 
						<el-text tag="p">
							Ctrl+G(分组) | Ctrl+Shift+G(取消分组)
						</el-text> 
						<el-text tag="p">
							Delete(删除)
						</el-text> 
						<el-text tag="p">
							方向键(移动)
						</el-text> 
						</div>
					</el-popover>
				</ElCol>
			</ElRow>
		</div>
		<div class="body" style="height: calc(100vh - 214px);">
			<!-- 左侧组件面板 -->
			<div class="components-panel" style="padding-right: 10px;" id="components-panel">
			  <ComponentToolbar 
				@add-component="onAddComponentFromToolbar"
				style="height: 100%;"
			  />
			</div>
					  
			<!-- 中间设计区域 -->
			<div class="design-area" ref="designAreaRef" @mousedown="onAreaMouseDown" @contextmenu.prevent  @dragstart="onDesignAreaDragStart" @dragover.prevent>
				<div v-if="dynamicVisualStore.pages.length === 0" class="empty-design-area">
					<i class="el-icon-document" style="font-size:48px;opacity:0.5;"></i>
					<p>暂无页面，请点击上方"新建页面"按钮</p>
				</div>
				<template v-else>
					<!-- 原有设计区内容 -->
					<div class="design-page-outer"
						:style="[
							pageStyle,
							{
							  position: 'absolute',
							  left: canvasOffset.x + 'px',
							  top: canvasOffset.y + 'px'
							}
						]"
					>
						<!-- 页面画布 -->
						<div
							class="design-page"
							ref="designPageRef"
							:style="{
								width: pageWidth + 'px',
								height: pageHeight + 'px',
								transform: `scale(${canvasScale})`,
								transformOrigin: 'left top',
								background: 'transparent'
							}"
							@mousedown="onDesignPageMouseDown"
							@contextmenu.prevent
							@dragover.prevent 
							@drop="dropComponent"
						>
							<!-- 框选选区 -->
							<div v-if="isBoxSelecting"
								class="box-select-rect"
								:style="{
									left: boxSelectRect.x + 'px',
									top: boxSelectRect.y + 'px',
									width: boxSelectRect.width + 'px',
									height: boxSelectRect.height + 'px'
								}"
							></div>
							<!-- :zIndex="selectedIds.includes(item.id) ? 1000 : 1" -->
							<MoveableBox
								v-for="item in dynamicVisualStore.currentComponents"
								:key="item.id"
								:id="item.id"
								:x="item.x"
								:y="item.y"
								:width="item.width"
								:height="item.height"
								:rotate="item.rotate || 0"
								:canvasScale="canvasScale"
								:canvasOffset="canvasOffset"
								:canvasWidth="pageWidth"
								:canvasHeight="pageHeight"
								:active="selectedIds.includes(item.id)"
								:hideHandles="hideHandles"
								:locked="item.locked === true"
								:visible="item.visible"
								:shank="moveableBoxShank"
								:enterAnimation="item.enterAnimation || '' "
								:leaveAnimation="item.leaveAnimation || '' "
								:animationDuration="item.animationDuration || '' "
								@update="onBoxUpdate"
								@dragStart="onBoxDragStart"
								@dragEnd="onBoxDragEnd"
								@mousedown="(e: MouseEvent) => handleBoxMouseDown(e, item.id)"
								@contextmenu="(e: MouseEvent) => handleBoxContextMenu(e, item)"
							>
								<div class="component-content" :visible="item.visible" :class="{ 'selected':( selectedIds.includes(item.id) && !dynamicVisualStore.debugMode) }" >
									<DynamicComponentRenderer
										:component-data="item"
									/>
								</div>
							</MoveableBox>
						  
							<!-- 辅助线 -->
							<template v-if="snapLines.x.length > 0">
								<div v-for="x in snapLines.x" :key="'snap-x-' + x" class="snap-line-x" :style="{ left: x + 'px', top: '0px', height: pageHeight + 'px' }" />
							</template>
								
							<template v-if="snapLines.y.length > 0">
								<div v-for="y in snapLines.y" :key="'snap-y-' + y" class="snap-line-y" :style="{ top: y + 'px', left: '0px', width: pageWidth + 'px' }" />
							</template>
							  
							<!-- 吸附点 -->
							<template v-if="snapLines.x.length > 0">
								<div v-for="x in snapLines.x" :key="'snap-dot-x-' + x" class="snap-dot" :style="{ left: (x - 3) + 'px', top: '-3px' }" />
							</template>
						  
							<template v-if="snapLines.y.length > 0">
								<div v-for="y in snapLines.y" :key="'snap-dot-y-' + y" class="snap-dot" :style="{ top: (y - 3) + 'px', left: '-3px' }" />
							</template>
						  
							<!--分组-->
							<div
								v-for="group in dynamicVisualStore.currentGroups"
								:key="`group-${group.id}`"
								class="group-container"
								:class="{ 'group-selected': selectedGroupIds.includes(group.id) }"
								v-show="(group.visible ?? true) && !dynamicVisualStore.debugMode "
								:style="{
								  position: 'absolute',
								  left: group.x + 'px',
								  top: group.y + 'px',
								  width: group.width + 'px',
								  height: group.height + 'px',
								  border: isGroupDragging && draggingGroupId === group.id ? '2px solid #67C23A' : selectedGroupIds.includes(group.id) ? '2px solid #E6A23C' : '2px dashed #409EFF',
								  borderRadius: '4px',
								  pointerEvents: 'auto',
								  userSelect: 'none',
								  zIndex: selectedGroupIds.includes(group.id) ? 10 : 5,cursor: group.locked ? '' : 'move',
								  boxShadow: isGroupDragging && draggingGroupId === group.id ? '0 0 8px #67C23A' : selectedGroupIds.includes(group.id) ? '0 0 8px #E6A23C' : ''
								}"
								@mousedown="(e: MouseEvent) => onGroupMouseDown(group, e)"
								@click="(e: MouseEvent) => selectGroup(group.id, e.ctrlKey || e.metaKey)"
								@contextmenu.prevent="(e: MouseEvent) => handleGroupContextMenu(e, group)"
								>
								<div
								  class="group-label"
								  :style="{ position: 'absolute', top: '-20px', left: '0', background: '#409EFF', color: 'white', padding: '2px 6px', borderRadius: '2px', fontSize: '12px', display: 'flex', alignItems: 'center' }"
								  @dblclick.stop="startEditGroupName(group)"
								>
									<template v-if="editingGroupId === group.id">
										<input
											:id="'group-name-input-' + group.id"
											v-model="editingGroupName"
											@blur="finishEditGroupName(group)"
											@keyup.enter="finishEditGroupName(group)"
											@keydown.stop
											style="width: 80px; font-size: 12px; border-radius: 2px; border: 1px solid #409EFF; padding: 2px 4px;"
										/>
									</template>
									<template v-else>
										{{ group.name }}
										<i class="el-icon-edit" style="margin-left:4px;cursor:pointer;" @click.stop="startEditGroupName(group)" title="重命名"></i>
									</template>
								</div>
							</div>
								
							<div v-if="dynamicVisualStore.currentComponents.length === 0" class="empty-design-area">
								<i class="el-icon-upload2"></i>
								<p>从左侧拖拽组件到此处开始设计</p>
							</div>
						</div>
					</div>
				</template>
				
				<!-- 缩放工具栏，移到 design-area 直接子元素，确保层级最高 -->
				<div class="zoom-toolbar">
					<el-button size="small" icon="Minus" @click="dynamicVisualStore.pages.length === 0 ? undefined : zoomOut()" :disabled="canvasScale <= 0.1 || dynamicVisualStore.pages.length === 0" circle />
					<el-slider
						v-model="canvasScale"
						:min="0.1"
						:max="3"
						:step="0.01"
						style="width: 120px; margin: 0 8px;"
						:show-tooltip="false"
						@focus="onSliderFocus"
						@blur="onSliderBlur"
						@keydown.stop
						:disabled="dynamicVisualStore.pages.length === 0"
					/>
					<el-button size="small" icon="Plus" @click="dynamicVisualStore.pages.length === 0 ? undefined : zoomIn()" :disabled="canvasScale >= 3 || dynamicVisualStore.pages.length === 0" circle />
					<span class="zoom-label">{{ Math.round(canvasScale * 100) }}%</span>
					<el-select
						v-model="quickZoom"
						placeholder="快捷"
						style="width: 70px; margin-left: 8px;"
						@change="setQuickZoom"
						size="small"
						:teleported="false"
						:disabled="dynamicVisualStore.pages.length === 0"
					>
						<el-option
						  v-for="item in quickZoomOptions"
						  :key="item.value"
						  :label="item.label"
						  :value="item.value"
						/>
					</el-select>
				</div>
						  
				<!-- 状态栏 -->
				<div class="status-bar">
					<div class="status-item">
						<i class="el-icon-info"></i>
						已选择 {{ selectedIds.length }} 个组件
					</div>
					<div class="status-item" v-if="selectedGroupIds.length > 0">
						<i class="el-icon-folder"></i>
						已选择 {{ selectedGroupIds.length }} 个分组
					</div>
					<div class="status-item" v-if="selectedIds.length === 1">
						<i class="el-icon-location"></i>
						X: {{ selectedComponent?.x || 0 }}, Y: {{ selectedComponent?.y || 0 }}
					</div>
					<div class="status-item" v-if="selectedIds.length === 1">
						<i class="el-icon-full-screen"></i>
						{{ selectedComponent?.width || 0 }} × {{ selectedComponent?.height || 0 }}
						</div>
					<div class="status-item">
						<i class="el-icon-folder"></i>
						{{ dynamicVisualStore.currentGroups.length }} 个分组
					</div>
					<div class="status-item">
						<i class="el-icon-refresh-left"></i>
						历史记录: {{ historyIndex + 1 }}/{{ history.length }}
					</div>
					<div class="status-item" v-if="snapLines.x.length > 0 || snapLines.y.length > 0">
						<i class="el-icon-magic-stick"></i>
						辅助线: {{ snapLines.x.length + snapLines.y.length }} 条
					</div>
					<div class="status-item" v-if="selectedIds.length > 1 && hasGroupedComponents">
						<i class="el-icon-arrow-up"></i>
						分组移动: 方向键
					</div>
					<div class="status-item" v-if="selectedGroupIds.length > 0">
						<i class="el-icon-arrow-up"></i>
						分组操作: Ctrl+Shift+G(取消分组)
					</div>
				</div>
			</div>
				
			<!-- 右侧属性面板 -->
			<PropertiesPanel
				v-if="dynamicVisualStore.pages.length > 0"
				:selected-ids="selectedIds"
				:selected-component="selectedComponent || ({} as ComponentInstance)"
				:batch-bg-color="batchBgColor"
				:batch-width="batchWidth"
				:batch-height="batchHeight"
				:batch-update-bg-color="batchUpdateBgColor"
				:batch-update-size="batchUpdateSize"
				:apply-batch-changes="applyBatchChanges"
				:reset-batch-changes="resetBatchChanges"
				:handle-component-config-change="handleComponentConfigChange"
				:page-title="pageTitle"
				:page-size="pageSize"
				:page-bg-color="pageBgColor"
				:page-bg-img="pageBgImg"
				:pageBindings="pageBindings"
				:fit-mode="fitMode"
				:filter="filter"
				:theme-color="themeColor"
				:components="dynamicVisualStore.currentComponents"
				:groups="dynamicVisualStore.currentGroups"
				:selectedGroup="currentSelectedGroup"
				@update:pageSize="(val:any) => pageSize = val"
				@update:pageBgColor="(val:any) => pageBgColor = val"
				@update:pageBgImg="(val:any) => pageBgImg = val"
				@update:fitMode="(val:any) => fitMode = val"
				@update:filter="(val:any) => Object.assign(filter, val)"
				@update:themeColor="(val:any) => themeColor = val"
				@selectComponentById="(id: string) => selectComponent(id, false)"
				@selectGroupById="(id: string) => selectGroup(id, false)"
				@update:pageTitle="(val:any) => { 
					pageTitle = val; 
					if (dynamicVisualStore.pages[dynamicVisualStore.currentPageIndex]) {
						dynamicVisualStore.pages[dynamicVisualStore.currentPageIndex].title = val;
					}
				}"
				@update:pageBindings="(val:any)=>{
					pageBindings=val;
				}"
				@update:groupBindings="(val:any)=>{
					if(currentSelectedGroup){
						currentSelectedGroup.bindings=val;
					}
				}"
				@menu:context="(event: MouseEvent, data: any, _node: any)=>{
					handleNodeContextMenu(event,data,_node)
				}"
			/>
			<!-- currentSelectedGroup?.bindings=val; -->
			<div v-else class="properties-panel empty">
				<i class="el-icon-document" style="font-size:36px;opacity:0.7;"></i>
				<p>暂无页面，请新建页面后编辑属性</p>
			</div>
		</div>
	</div>
	<ContextMenu ref="contextMenuRef" @structureContextMenu="handleStructureContextMenu"></ContextMenu>	
</ArtTableFullScreen>
</template>

<script setup lang="ts">
import { ref, reactive, computed, onMounted, nextTick, watch, onBeforeUnmount, type ComponentPublicInstance } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
  Plus,
  Minus,
  Delete
} from '@element-plus/icons-vue'
import MoveableBox from '@/components/visual/MoveableBox.vue'
import '@/assets/styles/markdown.scss'
import '@/assets/styles/one-dark-pro.scss'
import 'animate.css';
import { useRoute,useRouter } from 'vue-router';
import { useCommon } from '@/composables/useCommon'
import { useWindowSize } from '@vueuse/core'
import { componentManager } from '@/components/visual/util/DynamicComponentUtil'
import ComponentToolbar from '@/components/visual/ComponentToolbar.vue'
import ComponentConfigPanel from '@/components/visual/ComponentConfigPanel.vue'
import DynamicComponentRenderer from '@/components/visual/DynamicComponentRenderer.vue'
import ContextMenu from '@/components/visual/ContextMenu.vue'
import { useComponentDataModelStore } from '@/store/modules/component'
import PropertiesPanel from './widget/PropertiesPanel.vue'
import { getPageStyle } from '@/utils/ui/pageStyle'
import type { ComponentGroup, HistoryItem,PageData,PageFilter, ComponentInstance } from '@/types/visual-app'
import { useDynamicComponentStore } from '@/store/dynamicComponent'
import { useVisualStore } from '@/store/visual'
import { ProjectService } from '@/api/visual/projectApi'
const dynamicComponentStore = useDynamicComponentStore()
const dynamicVisualStore = useVisualStore()
const isSliderFocusing = ref(false);
const { containerMinHeight } = useCommon()
const { width } = useWindowSize()
const route = useRoute();
const router = useRouter();
const contextMenuRef=ref();

// 页面设置
const appTitle = ref<string>('')
const pageTitle = ref<string>('')
const pageSize = ref<string>('1920x1080')
const pageBgColor = ref<string>('#1a2b4c')
const pageBgImg = ref<string>('')
const pageBindings = ref<any[]>([])
const fitMode = ref<string>('contain')
const filter = ref<{hue:number;saturate:number;contrast:number;brightness:number;opacity:number;}>(
  {
    hue: 0,
    saturate: 100,
    contrast: 100,
    brightness: 100,
    opacity: 100
  }
)
const themeColor = ref<string>('#409EFF')
    
// 页面宽高联动
const pageWidth = computed(() => {
  if (typeof pageSize.value === 'string' && pageSize.value.includes('x')) {
	const [w] = pageSize.value.split('x').map(Number)
	return w || 1920
  }
  return 1920
})

const pageHeight = computed(() => {
  if (typeof pageSize.value === 'string' && pageSize.value.includes('x')) {
	const [, h] = pageSize.value.split('x').map(Number)
	return h || 1080
  }
  return 1080
})


// 处理 empty-values
const validEmptyValues = computed(() => {
  const values = new Set<any>([null, undefined, '',-1]) // 默认空值
  return Array.from(values)
})    

const moveableBoxShank=ref<boolean>(false)
const selectedIds = ref<string[]>([]) // 多选支持
const selectedGroupIds = ref<string[]>([]) // 选中的分组ID
const history = ref<HistoryItem[]>([]) // 历史记录

const historyIndex = ref(-1) // 当前历史位置
const maxHistorySize = 50 // 最大历史记录数
const dragComponentType = ref<string | null>(null)
    
    // 画布缩放
    const canvasScale = ref(1)
    const isCanvasInteracting = ref(false)
    const hideHandles = ref(false) // 控制是否隐藏手柄
    const designPageRef = ref<HTMLElement | null>(null)
    const designAreaRef = ref<HTMLElement | null>(null)
    const autoFitCanvas = () => {
      nextTick(() => {
        const container = designPageRef.value
        if (!container) return
        const w = container.clientWidth
        const h = container.clientHeight
        const scaleW = w / pageWidth.value
        const scaleH = h / pageHeight.value
        canvasScale.value = Math.min(scaleW, scaleH, 1)
      })
    }
    
    // 计算当前选中的组件（支持多选）
    const selectedComponents = computed(() => {
      return dynamicVisualStore.currentComponents.filter(c => selectedIds.value.includes(c.id))
    })
    
    // 计算当前选中的组件（单选兼容）
const selectedComponent = computed(() => {
      if (selectedIds.value.length === 1) {
        return dynamicVisualStore.currentComponents.find(c => c.id === selectedIds.value[0]) || null
      }
      return null
    })

const handleComponentConfigChange = (updatedComponent: ComponentInstance) => {
  const idx = dynamicVisualStore.currentComponents.findIndex(c => c.id === updatedComponent.id)
  if (idx !== -1) {
    // 保持响应式
    dynamicVisualStore.currentComponents[idx] = { ...updatedComponent }
  }
}
    
    // 计算是否有分组组件被选中
    const hasGroupedComponents = computed(() => {
      return selectedComponents.value.some(c => c.isGrouped) || selectedGroupIds.value.length > 0
    })
    
    // 计算当前选中的分组
    const selectedGroups = computed(() => {
      return dynamicVisualStore.currentGroups.filter(g => selectedGroupIds.value.includes(g.id))
    })
    
    // 计算是否有分组被选中
    const hasSelectedGroups = computed(() => {
      return selectedGroupIds.value.length > 0
    })
    
    // 批量编辑相关变量
    const batchBgColor = ref('#ffffff')
    const batchWidth = ref(200)
    const batchHeight = ref(150)
    const batchChanges = ref<{ bgColor?: string; width?: number; height?: number }>({})
    
    // 历史记录管理
    const addHistory = (type: HistoryItem['type'], data: any) => {
      // 删除当前位置之后的历史记录
      history.value = history.value.slice(0, historyIndex.value + 1)
      // 添加新的历史记录
      history.value.push({
        type,
        data,
        timestamp: Date.now()
      })
      // 限制历史记录数量
      if (history.value.length > maxHistorySize) {
        history.value.shift() // 移除最早的
        historyIndex.value = Math.max(0, historyIndex.value - 1)
      } else {
        historyIndex.value++
      }
    }
    
    // 批量更新背景颜色
    const batchUpdateBgColor = (color: string | null) => {
      if (color) {
        batchChanges.value.bgColor = color
      }
    }
    
    // 批量更新尺寸
    const batchUpdateSize = () => {
      batchChanges.value.width = batchWidth.value
      batchChanges.value.height = batchHeight.value
    }
    
    // 应用批量更改
    const applyBatchChanges = () => {
      if (Object.keys(batchChanges.value).length === 0) return
      
      selectedIds.value.forEach(id => {
        const comp = dynamicVisualStore.currentComponents.find(c => c.id === id)
        if (comp) {
          const oldState = { ...comp }
          Object.assign(comp, batchChanges.value)
          addHistory('update', { id, oldState, newState: { ...comp } })
        }
      })
      
      // 清空批量更改
      batchChanges.value = {}
      ElMessage.success('批量更改已应用')
    }
    
    // 重置批量更改
    const resetBatchChanges = () => {
      batchChanges.value = {}
      batchBgColor.value = '#ffffff'
      batchWidth.value = 200
      batchHeight.value = 150
    }
    
    // 撤销功能
    const undo = () => {
      if (historyIndex.value >= 0) {
        // 隐藏手柄
        hideHandles.value = true
        
        const item = history.value[historyIndex.value]
        if (item) {
          switch (item.type) {
            case 'add':
              // 撤销添加：删除组件
              const addIndex = dynamicVisualStore.currentComponents.findIndex(c => c.id === item.data.id)
              if (addIndex !== -1) {
                dynamicVisualStore.currentComponents.splice(addIndex, 1)
                selectedIds.value = selectedIds.value.filter(id => id !== item.data.id)
              }
              break
            case 'delete':
              // 撤销删除：恢复组件
              dynamicVisualStore.currentComponents.push(item.data)
              break
            case 'update':
              // 撤销更新：恢复组件状态
              const updateIndex = dynamicVisualStore.currentComponents.findIndex(c => c.id === item.data.id)
              if (updateIndex !== -1) {
                Object.assign(dynamicVisualStore.currentComponents[updateIndex], item.data.oldState)
              }
              break
            case 'group':
              // 撤销分组：取消分组（不添加历史记录）
              const groupToUngroup = dynamicVisualStore.currentGroups.find(g => g.id === item.data.groupId)
              if (groupToUngroup) {
                // 更新组件分组信息
                groupToUngroup.componentIds.forEach(compId => {
                  const comp = dynamicVisualStore.currentComponents.find(c => c.id === compId)
                  if (comp) {
                    comp.groupId = undefined
                    comp.isGrouped = false
                  }
                })
                
                // 删除分组
                const groupIndex = dynamicVisualStore.currentGroups.findIndex(g => g.id === item.data.groupId)
                if (groupIndex !== -1) {
					dynamicVisualStore.currentGroups.splice(groupIndex, 1)
                }
                
                // 从选中分组中移除
                selectedGroupIds.value = selectedGroupIds.value.filter(id => id !== item.data.groupId)
                
                // 选中被取消分组的组件
                selectedIds.value = item.data.componentIds
              }
              break
            case 'ungroup':
              // 撤销取消分组：恢复分组（不添加历史记录）
              const groupId = item.data.groupId
              const componentIds = item.data.componentIds
              
              if (componentIds && componentIds.length >= 2) {
                const groupComponents = dynamicVisualStore.currentComponents.filter(c => componentIds.includes(c.id))
                
                // 计算分组边界
                const minX = Math.min(...groupComponents.map(c => c.x))
                const minY = Math.min(...groupComponents.map(c => c.y))
                const maxX = Math.max(...groupComponents.map(c => c.x + c.width))
                const maxY = Math.max(...groupComponents.map(c => c.y + c.height))
                
                // 创建分组
                const group: ComponentGroup = {
                  id: groupId,
                  name: `分组 ${dynamicVisualStore.currentGroups.length + 1}`,
                  componentIds,
                  x: minX,
                  y: minY,
                  width: maxX - minX,
                  height: maxY - minY
                }
                
				dynamicVisualStore.currentGroups.push(group)
                
                // 更新组件分组信息
                groupComponents.forEach(comp => {
                  comp.groupId = groupId
                  comp.isGrouped = true
                })
                
                // 选中新创建的分组
                selectedGroupIds.value = [groupId]
                
                // 清空组件选择
                selectedIds.value = []
              }
              break
            case 'groupMove':
              // 撤销移动分组：恢复分组边框和组内组件位置
              const groupToRestore = dynamicVisualStore.currentGroups.find(g => g.id === item.data.groupId)
              if (groupToRestore && item.data.componentStates) {
                // 恢复分组边框位置
                groupToRestore.x = item.data.oldState.x
                groupToRestore.y = item.data.oldState.y
                
                // 恢复组内组件位置
                Object.entries(item.data.componentStates).forEach(([compId, states]: [string, any]) => {
                  const comp = dynamicVisualStore.currentComponents.find(c => c.id === compId)
                  if (comp) {
                    comp.x = states.oldState.x
                    comp.y = states.oldState.y
                  }
                })
                
                // 确保分组保持选中状态
                if (!selectedGroupIds.value.includes(groupToRestore.id)) {
                  selectedGroupIds.value = [groupToRestore.id]
                }
              }
              break
            case 'reorder':
              // 撤销重新排序：恢复原始顺序
              const oldOrderIds = item.data.oldOrder as string[]
              const reorderedComponents = oldOrderIds.map((id: string) => dynamicVisualStore.currentComponents.find(c => c.id === id)).filter(Boolean)
              dynamicVisualStore.currentComponents = reorderedComponents as ComponentInstance[]
              break
          }
          historyIndex.value--
        }
        
        // 延迟还原手柄，给动画一些时间
        setTimeout(() => {
          hideHandles.value = false
        }, 200)
      }
    }
    
    // 重做功能
    const redo = () => {
      if (historyIndex.value < history.value.length - 1) {
        // 隐藏手柄
        hideHandles.value = true
        
        historyIndex.value++
        const item = history.value[historyIndex.value]
        if (item) {
          switch (item.type) {
            case 'add':
              // 重做添加：添加组件
              dynamicVisualStore.currentComponents.push(item.data)
              break
            case 'delete':
              // 重做删除：删除组件
              const deleteIndex = dynamicVisualStore.currentComponents.findIndex(c => c.id === item.data.id)
              if (deleteIndex !== -1) {
                dynamicVisualStore.currentComponents.splice(deleteIndex, 1)
                selectedIds.value = selectedIds.value.filter(id => id !== item.data.id)
              }
              break
            case 'update':
              // 重做更新：应用新状态
              const redoIndex = dynamicVisualStore.currentComponents.findIndex(c => c.id === item.data.id)
              if (redoIndex !== -1) {
                Object.assign(dynamicVisualStore.currentComponents[redoIndex], item.data.newState)
              }
              break
            case 'group':
              // 重做分组：创建分组（不添加历史记录）
              const componentIds = item.data.componentIds
              const groupId = item.data.groupId
              
              if (componentIds && componentIds.length >= 2) {
                const groupComponents = dynamicVisualStore.currentComponents.filter(c => componentIds.includes(c.id))
                
                // 计算分组边界
                const minX = Math.min(...groupComponents.map(c => c.x))
                const minY = Math.min(...groupComponents.map(c => c.y))
                const maxX = Math.max(...groupComponents.map(c => c.x + c.width))
                const maxY = Math.max(...groupComponents.map(c => c.y + c.height))
                
                // 创建分组
                const group: ComponentGroup = {
                  id: groupId,
                  name: `分组 ${dynamicVisualStore.currentGroups.length + 1}`,
                  componentIds,
                  x: minX,
                  y: minY,
                  width: maxX - minX,
                  height: maxY - minY
                }
                
                dynamicVisualStore.currentGroups.push(group)
                
                // 更新组件分组信息
                groupComponents.forEach(comp => {
                  comp.groupId = groupId
                  comp.isGrouped = true
                })
                
                // 选中新创建的分组
                selectedGroupIds.value = [groupId]
                
                // 清空组件选择
                selectedIds.value = []
              }
              break
            case 'ungroup':
              // 重做取消分组：取消分组（不添加历史记录）
              const groupToUngroup = dynamicVisualStore.currentGroups.find(g => g.id === item.data.groupId)
              if (groupToUngroup) {
                // 更新组件分组信息
                groupToUngroup.componentIds.forEach(compId => {
                  const comp = dynamicVisualStore.currentComponents.find(c => c.id === compId)
                  if (comp) {
                    comp.groupId = undefined
                    comp.isGrouped = false
                  }
                })
                
                // 删除分组
                const groupIndex = dynamicVisualStore.currentGroups.findIndex(g => g.id === item.data.groupId)
                if (groupIndex !== -1) {
                  dynamicVisualStore.currentGroups.splice(groupIndex, 1)
                }
                
                // 从选中分组中移除
                selectedGroupIds.value = selectedGroupIds.value.filter(id => id !== item.data.groupId)
                
                // 选中被取消分组的组件
                selectedIds.value = item.data.componentIds
              }
              break
            case 'groupMove':
              // 重做移动分组：应用分组边框和组内组件位置
              const groupToApply = dynamicVisualStore.currentGroups.find(g => g.id === item.data.groupId)
              if (groupToApply && item.data.componentStates) {
                // 应用分组边框位置
                groupToApply.x = item.data.newState.x
                groupToApply.y = item.data.newState.y
                
                // 应用组内组件位置
                Object.entries(item.data.componentStates).forEach(([compId, states]: [string, any]) => {
                  const comp = dynamicVisualStore.currentComponents.find(c => c.id === compId)
                  if (comp) {
                    comp.x = states.newState.x
                    comp.y = states.newState.y
                  }
                })
                
                // 确保分组保持选中状态
                if (!selectedGroupIds.value.includes(groupToApply.id)) {
                  selectedGroupIds.value = [groupToApply.id]
                }
              }
              break
            case 'reorder':
              // 重做重新排序：应用新顺序
              const redoOrderIds = item.data.newOrder as string[]
              const redoComponents = redoOrderIds.map((id: string) => dynamicVisualStore.currentComponents.find(c => c.id === id)).filter(Boolean)
              dynamicVisualStore.currentComponents = redoComponents as ComponentInstance[]
              break
          }
        }
        
        // 延迟还原手柄，给动画一些时间
        setTimeout(() => {
          hideHandles.value = false
        }, 200)
      }
    }
    
    // 分组功能
    const groupComponents = (componentIds: string[]) => {
      if (componentIds.length < 2) return
      
      const groupId = Date.now().toString()
      const groupComponents = dynamicVisualStore.currentComponents.filter(c => componentIds.includes(c.id))
      
      // 计算分组边界
      const minX = Math.min(...groupComponents.map(c => c.x))
      const minY = Math.min(...groupComponents.map(c => c.y))
      const maxX = Math.max(...groupComponents.map(c => c.x + c.width))
      const maxY = Math.max(...groupComponents.map(c => c.y + c.height))
      
      // 创建分组
      const group: ComponentGroup = {
        id: groupId,
        name: `分组 ${dynamicVisualStore.currentGroups.length + 1}`,
        componentIds,
        x: minX,
        y: minY,
        width: maxX - minX,
        height: maxY - minY,
		visible:true
      }
      
      dynamicVisualStore.currentGroups.push(group)
      
      // 更新组件分组信息
      groupComponents.forEach(comp => {
        comp.groupId = groupId
        comp.isGrouped = true
      })
      
      // 选中新创建的分组
      selectedGroupIds.value = [groupId]
      
      // 添加到历史记录
      addHistory('group', { groupId, componentIds })
    }
    
    // 取消分组功能
    const ungroupComponents = (groupId: string) => {
      console.log('Ungrouping components for group ID:', groupId)
      const group = dynamicVisualStore.currentGroups.find(g => g.id === groupId)
      if (!group) {
        console.log('Group not found:', groupId)
        return
      }
      
      console.log('Found group:', group)
      
      // 更新组件分组信息
      group.componentIds.forEach(compId => {
        const comp = dynamicVisualStore.currentComponents.find(c => c.id === compId)
        if (comp) {
          comp.groupId = undefined
          comp.isGrouped = false
          console.log('Updated component:', compId, 'removed from group')
        }
      })
      
      // 删除分组
      const groupIndex = dynamicVisualStore.currentGroups.findIndex(g => g.id === groupId)
      if (groupIndex !== -1) {
        dynamicVisualStore.currentGroups.splice(groupIndex, 1)
        console.log('Removed group from groups array')
      }
      
      // 从选中分组中移除
      selectedGroupIds.value = selectedGroupIds.value.filter(id => id !== groupId)
      
      // 添加到历史记录
      addHistory('ungroup', { groupId, componentIds: group.componentIds })
      //console.log('Ungroup operation completed successfully')
    }
    
    // 取消选中组件的分组
    const ungroupSelected = () => {
      //console.log('Ungrouping selected components and groups')
      
      // 如果有选中的分组，优先处理分组
      if (selectedGroupIds.value.length > 0) {
        //console.log('Ungrouping selected groups:', selectedGroupIds.value)
        selectedGroupIds.value.forEach(groupId => {
          ungroupComponents(groupId)
        })
        // 清空选中的分组
        selectedGroupIds.value = []
        return
      }
      
      // 否则处理选中的组件
      const groupedComponents = selectedComponents.value.filter(c => c.isGrouped)
      if (groupedComponents.length > 0) {
        console.log('Ungrouping selected components:', groupedComponents.map(c => c.id))
        const groupIds = [...new Set(groupedComponents.map(c => c.groupId).filter(Boolean))]
        groupIds.forEach(groupId => ungroupComponents(groupId!))
      }
	  currentSelectedGroup.value=undefined;
    }
    
    // 全选功能
    const selectAll = () => {
      selectedIds.value = dynamicVisualStore.currentComponents.map(c => c.id)
      selectedGroupIds.value = dynamicVisualStore.currentGroups.map(g => g.id)
    }
    
    // 取消选择
    const clearSelection = () => {
      selectedIds.value = []
      selectedGroupIds.value = []
    }
    
    // 删除选中组件
    const deleteSelected = () => {
      // 如果有选中的分组，优先删除分组
      if (selectedGroupIds.value.length > 0) {
        selectedGroupIds.value.forEach(groupId => {
          const group = dynamicVisualStore.currentGroups.find(g => g.id === groupId)
          if (group) {
            // 删除组内组件
            dynamicVisualStore.currentComponents = dynamicVisualStore.currentComponents.filter(c => !group.componentIds.includes(c.id))
            // 删除分组
            const groupIdx = dynamicVisualStore.currentGroups.findIndex(g => g.id === groupId)
            if (groupIdx !== -1) dynamicVisualStore.currentGroups.splice(groupIdx, 1)
          }
        })
        // 清空选中的分组
        selectedGroupIds.value = []
        return
      }
      // 否则删除选中的组件
      if (selectedIds.value.length === 0) return
      // 添加到历史记录
      selectedIds.value.forEach(id => {
        const comp = dynamicVisualStore.currentComponents.find(c => c.id === id)
        if (comp) {
          addHistory('delete', comp)
        }
      })
      // 删除组件
      dynamicVisualStore.currentComponents = dynamicVisualStore.currentComponents.filter(c => !selectedIds.value.includes(c.id))
      selectedIds.value = []
    }
    
    // 复制选中组件
    const copySelected = () => {
		if (selectedIds.value.length === 0) return
		const copiedComponents: ComponentInstance[] = []
		selectedIds.value.forEach(id => {
        const comp =dynamicVisualStore.currentComponents.find(c => c.id === id)
        if (comp) {
			const copy: ComponentInstance = {
				...comp, 
				id: (Date.now() + Math.random()).toString(),
				x: comp.x + 30, 
				y: comp.y + 30,
				groupId: undefined,
				isGrouped: false,
				title: comp.title ?? '',
				bgColor: comp.bgColor ?? '#ffffff'
			}
			copiedComponents.push(copy)
        }
	})
      
      // 添加到历史记录
      copiedComponents.forEach(comp => {
        addHistory('add', comp)
      })
      
      // 添加复制的组件
      dynamicVisualStore.currentComponents.push(...copiedComponents)
      
      // 选中复制的组件
      selectedIds.value = copiedComponents.map(c => c.id)
      
      nextTick(() => {})
}


const hanldeDynamicComponentRendererVisible=(state :boolean)=>{
	console.log('hanldeDynamicComponentRendererVisible=',state)
}


// 放置组件（到设计区域）
const dropComponent = (event: DragEvent) => {
	if(dynamicVisualStore.debugMode==true) return;
	
      event.preventDefault();
      
      // 优先从 dataTransfer 获取数据
      let type = '';
      let componentId = '';
      let componentName = '';
      
      if (event.dataTransfer) {
        try {
          type = event.dataTransfer.getData('component-type') || '';
          componentId = event.dataTransfer.getData('component-id') || '';
          componentName = event.dataTransfer.getData('component-name') || '';
        } catch (error) {
          console.error('解析拖拽数据时出错:', error)
        }
      }
      
      // 如果 dataTransfer 中没有数据，尝试使用 dragComponentType
      if (!type && dragComponentType.value) {
        type = dragComponentType.value;
      }
      
      if (!type) {
        return;
      }
    
      const designArea = designPageRef.value;
      if (!designArea) {
        return;
      }
      const rect = designArea.getBoundingClientRect();
      
      // 计算在原始画布坐标中的位置（只需除以缩放）
      const x = (event.clientX - rect.left) / canvasScale.value - 50;
      const y = (event.clientY - rect.top) / canvasScale.value - 25;
  
  // 创建新组件
	const newComponent: ComponentInstance = {
      id: (Date.now() + Math.random()).toString(),
      componentId,
      type,
      x,
      y,
      width: 200,
      height: 150,
      title: componentName || `新${type}组件`,
      bgColor: '#ffffff',
      config:  {},
	  bindings:[],
	  visible:true,
	  animationDuration:0.5
	};
	
      dynamicVisualStore.currentComponents.push(newComponent);
      selectedIds.value = [newComponent.id];
      dragComponentType.value = null;
      addHistory('add', newComponent);
      nextTick(() => {});
  }
  
// 从工具栏添加组件
const onAddComponentFromToolbar = (_componentId:string,_type: string) => {
	
}

// 移除组件
const removeComponent = (id: string) => {
  const index = dynamicVisualStore.currentComponents.findIndex(c => c.id === id)
  if (index !== -1) {
        const comp = dynamicVisualStore.currentComponents[index]
        // 添加到历史记录
        addHistory('delete', comp)
		dynamicVisualStore.currentComponents.splice(index, 1)
        selectedIds.value = selectedIds.value.filter(selectedId => selectedId !== id)
  }
}

// 更新组件
function updateComponent(id: string, data: any) {
	const comp = dynamicVisualStore.currentComponents.find(c => c.id === id)
	if (!comp) {
		console.error('updateComponent: 未找到组件', id, dynamicVisualStore.currentComponents.map(c => c.id))
		return;
	}
   // const oldState = { ...comp }
	Object.assign(comp, data)
   // addHistory('update', { id, oldState, newState: { ...comp } })
	dynamicVisualStore.currentComponents = [...dynamicVisualStore.currentComponents]
}


// 保存项目（多页面）
const saveProject = async () => {
  // 保存当前页面状态
  
  dynamicVisualStore.setPages
  
  dynamicVisualStore.pages[dynamicVisualStore.currentPageIndex] = {
    ...dynamicVisualStore.pages[dynamicVisualStore.currentPageIndex],
        title: pageTitle.value,
        pageSize: pageSize.value,
        pageBgColor: pageBgColor.value,
    pageBgImg: pageBgImg.value,
    fitMode: fitMode.value,
    filter: filter.value,
    themeColor: themeColor.value,
    components: dynamicVisualStore.currentComponents,
    groups: dynamicVisualStore.currentGroups,
    history:[],//history.value
    historyIndex:-1,//historyIndex.value
	bindings:pageBindings.value
  }
  const projectData = {
    pages: dynamicVisualStore.pages,
      version: '1.0.0',
      schema: 'dashboard-v1'
    }
  console.log('=== 多页面项目保存数据结构 ===')
  console.log('项目数据:', projectData)
  console.log('JSON格式:', JSON.stringify(projectData, null, 2))
  const projectId = typeof route.query.id === 'string' ? route.query.id : '';
  await ProjectService.savePages(projectId,projectData);
  console.log('projectId=',projectId)

  ElMessage.success('多页面项目数据结构已输出到控制台')
}

// 预览
function preview() {
  // 构建预览数据
  const previewData = {
    projectInfo: {
      title: pageTitle.value,
      pageWidth: pageWidth.value,
      pageHeight: pageHeight.value,
      pageBgColor: pageBgColor.value
    },
    components: dynamicVisualStore.currentComponents.map(comp => ({
      id: comp.id,
      type: comp.type,
      x: comp.x,
      y: comp.y,
      width: comp.width,
      height: comp.height,
      title: comp.title,
      bgColor: comp.bgColor,
      rotate: comp.rotate || 0,
      chartType: comp.chartType,
      showLegend: comp.showLegend,
      valueFormat: comp.valueFormat,
      groupId: comp.groupId,
      isGrouped: comp.isGrouped
    })),
    groups: dynamicVisualStore.currentGroups.map(group => ({
      id: group.id,
      name: group.name,
      componentIds: group.componentIds,
      x: group.x,
      y: group.y,
      width: group.width,
      height: group.height
    }))
  }
  // 存储到 localStorage
  localStorage.setItem('dashboardPreviewData', JSON.stringify(previewData))
  // 新窗口打开预览页面
  window.open('/visual-app/project/preview', '_blank', 'width=1920,height=1080,scrollbars=yes,resizable=yes')
}

// 发布
const publish = () => {
  ElMessage.success('项目发布成功')
}

// 导出
const exportProject = () => {
  ElMessage.info('导出功能开发中')
}

    // 缩放工具栏
    const zoomIn = () => {
      isCanvasInteracting.value = true
      hideHandles.value = true
      canvasScale.value = Math.min(3, +(canvasScale.value + 0.1).toFixed(2))
      centerCanvas()
      setTimeout(() => { 
        isCanvasInteracting.value = false 
        hideHandles.value = false
      }, 200)
    }
    const zoomOut = () => {
      isCanvasInteracting.value = true
      hideHandles.value = true
      canvasScale.value = Math.max(0.1, +(canvasScale.value - 0.1).toFixed(2))
      centerCanvas()
      setTimeout(() => { 
        isCanvasInteracting.value = false 
        hideHandles.value = false
      }, 200)
    }
    
    
    const quickZoomOptions = [
      { label: '10%', value: 0.1 },
      { label: '25%', value: 0.25 },
      { label: '50%', value: 0.5 },
      { label: '75%', value: 0.75 },
      { label: '100%', value: 1 },
      { label: '150%', value: 1.5 },
      { label: '200%', value: 2 },
      { label: '300%', value: 3 }
    ]
    const quickZoom = ref<number>(1)
    const setQuickZoom = (val: number) => {
      isCanvasInteracting.value = true
      hideHandles.value = true
      canvasScale.value = val
      quickZoom.value = val // 同步下拉框显示
      centerCanvas()
      setTimeout(() => { 
        isCanvasInteracting.value = false 
        hideHandles.value = false
      }, 200)
    }
    watch(canvasScale, (val) => {
      quickZoom.value = val // 拖动滑块时同步下拉框
      centerCanvas()
    })
    
    function sliderThumbDragStart() {
      isCanvasInteracting.value = true
      hideHandles.value = true
      const clear = () => {
        isCanvasInteracting.value = false
        hideHandles.value = false
        window.removeEventListener('mouseup', clear)
        window.removeEventListener('touchend', clear)
      }
      window.addEventListener('mouseup', clear)
      window.addEventListener('touchend', clear)
    }
    
    // 键盘方向键移动选中组件
    const moveStep = 1
    const moveStepFast = 10
    function moveSelectedComponent(dx: number, dy: number, targetId?: string) {
      const targetIds = targetId ? [targetId] : selectedIds.value
      if (targetIds.length === 0) return
      
      
      // 记录移动前的状态（用于历史记录）
      const oldStates: Record<string, { x: number, y: number }> = {}
      
      targetIds.forEach(id => {
        const comp = dynamicVisualStore.currentComponents.find(c => c.id === id)
        if (!comp) return
        
        // 记录原始位置
        oldStates[id] = { x: comp.x, y: comp.y }
        
        const maxX = pageWidth.value - comp.width
        const maxY = pageHeight.value - comp.height
        comp.x = Math.max(0, Math.min(comp.x + dx, maxX))
        comp.y = Math.max(0, Math.min(comp.y + dy, maxY))
      })
      
      // 添加到历史记录
      Object.entries(oldStates).forEach(([compId, oldState]) => {
        const comp = dynamicVisualStore.currentComponents.find(c => c.id === compId)
        if (comp) {
          addHistory('update', { 
            id: compId, 
            oldState, 
            newState: { x: comp.x, y: comp.y } 
          })
        }
      })
      
    }
    
    // 键盘方向键移动分组
    function moveSelectedGroup(dx: number, dy: number) {
      // 获取选中的分组
      const groupIdsToMove = new Set<string>()
      
      // 从选中的分组ID中获取
      if (selectedGroupIds.value.length > 0) {
        selectedGroupIds.value.forEach(id => groupIdsToMove.add(id))
      }
      
      // 从选中的组件中获取分组ID
      selectedIds.value.forEach(id => {
        const comp = dynamicVisualStore.currentComponents.find(c => c.id === id)
        if (comp && comp.isGrouped && comp.groupId) {
          groupIdsToMove.add(comp.groupId)
        }
      })
      
      if (groupIdsToMove.size === 0) return
      
      // 记录移动前的状态（用于历史记录）
      const oldStates: Record<string, { x: number, y: number }> = {}
      
      groupIdsToMove.forEach(groupId => {
        const group = dynamicVisualStore.currentGroups.find(g => g.id === groupId)
        if (!group) return
        
        // 记录组内组件的原始位置
        group.componentIds.forEach(compId => {
          const comp = dynamicVisualStore.currentComponents.find(c => c.id === compId)
          if (comp) {
            oldStates[compId] = { x: comp.x, y: comp.y }
          }
        })
        
        // 计算分组边界
        const minX = 0
        const minY = 0
        const maxX = pageWidth.value - group.width
        const maxY = pageHeight.value - group.height
        
        // 计算目标位置
        const targetX = Math.max(minX, Math.min(group.x + dx, maxX))
        const targetY = Math.max(minY, Math.min(group.y + dy, maxY))
        
        // 实际偏移量
        const realDx = targetX - group.x
        const realDy = targetY - group.y
        
        // 移动组内组件
        group.componentIds.forEach(compId => {
          const comp = dynamicVisualStore.currentComponents.find(c => c.id === compId)
          if (comp) {
            comp.x += realDx
            comp.y += realDy
          }
        })
        
        // 移动分组边框
        group.x = targetX
        group.y = targetY
      })
      
      // 添加到历史记录
      Object.entries(oldStates).forEach(([compId, oldState]) => {
        const comp = dynamicVisualStore.currentComponents.find(c => c.id === compId)
        if (comp) {
          addHistory('update', { 
            id: compId, 
            oldState, 
            newState: { x: comp.x, y: comp.y } 
          })
        }
      })
    }
    
    // 选中组件
    const selectComponent = (id: string, multiSelect = false) => {
      if (multiSelect) {
        // 多选模式
        if (selectedIds.value.includes(id)) {
          selectedIds.value = selectedIds.value.filter(selectedId => selectedId !== id)
        } else {
          selectedIds.value.push(id)
        }
      } else {
        // 单选模式
        selectedIds.value = [id]
      }
    }
    
	// 选中分组
	const currentSelectedGroup=ref<ComponentGroup>();
    const selectGroup = (groupId: string, multiSelect = false) => {
		if(dynamicVisualStore.debugMode==true){
			return;
		}
		currentSelectedGroup.value=dynamicVisualStore.currentGroups.find(g => g.id === groupId);
		
      console.log('Selecting group:', groupId, 'MultiSelect:', multiSelect)
      if (multiSelect) {
        // 多选模式
        if (selectedGroupIds.value.includes(groupId)) {
          selectedGroupIds.value = selectedGroupIds.value.filter(id => id !== groupId)
        } else {
          selectedGroupIds.value.push(groupId)
        }
      } else {
        // 单选模式
        selectedGroupIds.value = [groupId]
        // 同时选中组内的所有组件
        const group = dynamicVisualStore.currentGroups.find(g => g.id === groupId)
        if (group) {
          selectedIds.value = [...group.componentIds]
        }
      }
      console.log('Selected groups:', selectedGroupIds.value)
    }
    
    // 层级调整
    function bringToFront() {
      if (selectedIds.value.length === 0) return
      const originalOrder = [...dynamicVisualStore.currentComponents]
      // 未选中的在前，选中的在后，且保持原顺序
      const remain = dynamicVisualStore.currentComponents.filter(c => !selectedIds.value.includes(c.id))
      const selected = dynamicVisualStore.currentComponents.filter(c => selectedIds.value.includes(c.id))
      dynamicVisualStore.currentComponents = [...remain, ...selected]
      addHistory('reorder', {
        oldOrder: originalOrder.map(c => c.id),
        newOrder: dynamicVisualStore.currentComponents.map(c => c.id)
      })
    }
    function sendToBack() {
      if (selectedIds.value.length === 0) return
      const originalOrder = [...dynamicVisualStore.currentComponents]
      // 选中的在前，未选中的在后，且保持原顺序
      const remain = dynamicVisualStore.currentComponents.filter(c => !selectedIds.value.includes(c.id))
      const selected = dynamicVisualStore.currentComponents.filter(c => selectedIds.value.includes(c.id))
      dynamicVisualStore.currentComponents = [...selected, ...remain]
      addHistory('reorder', {
        oldOrder: originalOrder.map(c => c.id),
        newOrder: dynamicVisualStore.currentComponents.map(c => c.id)
      })
    }
    function moveUp() {
      if (selectedIds.value.length === 0) return
      const originalOrder = [...dynamicVisualStore.currentComponents]
      let i = dynamicVisualStore.currentComponents.length - 2
      let hasChanges = false
      while (i >= 0) {
        const cur = dynamicVisualStore.currentComponents[i]
        const next = dynamicVisualStore.currentComponents[i + 1]
        if (selectedIds.value.includes(cur.id) && !selectedIds.value.includes(next.id)) {
          dynamicVisualStore.currentComponents[i] = next
          dynamicVisualStore.currentComponents[i + 1] = cur
          hasChanges = true
          i++ // 跳过已交换的
        }
        i--
      }
      if (hasChanges) {
        addHistory('reorder', {
          oldOrder: originalOrder.map(c => c.id),
          newOrder: dynamicVisualStore.currentComponents.map(c => c.id)
        })
      }
    }
    function moveDown() {
      if (selectedIds.value.length === 0) return
      const originalOrder = [...dynamicVisualStore.currentComponents]
      let i = 1
      let hasChanges = false
      while (i < dynamicVisualStore.currentComponents.length) {
        const cur = dynamicVisualStore.currentComponents[i]
        const prev = dynamicVisualStore.currentComponents[i - 1]
        if (selectedIds.value.includes(cur.id) && !selectedIds.value.includes(prev.id)) {
          dynamicVisualStore.currentComponents[i] = prev
          dynamicVisualStore.currentComponents[i - 1] = cur
          hasChanges = true
          i++ // 跳过已交换的
        }
        i++
      }
      if (hasChanges) {
        addHistory('reorder', {
          oldOrder: originalOrder.map(c => c.id),
          newOrder: dynamicVisualStore.currentComponents.map(c => c.id)
        })
      }
    }
	
	// 显示/隐藏
	const isSelectedVisible = computed(() => {
	  if (selectedIds.value.length === 1) {
	    const comp = dynamicVisualStore.currentComponents.find(c => c.id === selectedIds.value[0])
	    return comp?.visible === true
	  }
	  return false
	})
	
	function toggleVisible() {
	  if (selectedIds.value.length === 1) {
	    const comp = dynamicVisualStore.currentComponents.find(c => c.id === selectedIds.value[0])
	    if (comp) {
	      comp.visible = !comp.visible
	    }
	  }
	}
	
    // 扩展键盘事件处理
    function onKeyDown(e: KeyboardEvent) {
		if(dynamicVisualStore.debugMode==true) return;
		const target = e.target as HTMLElement
		if(target && target.tagName==="INPUT" || target && target.tagName=='TEXTAREA'){
			return;
		}
		if (dynamicVisualStore.pages.length === 0) return;
		if (isSliderFocusing.value) {
		return;
		}
      // Ctrl+A: 全选
      if (e.ctrlKey && e.key === 'a') {
        e.preventDefault()
        selectAll()
        return
      }
      
      // Ctrl+Z: 撤销
      if (e.ctrlKey && e.key === 'z') {
        e.preventDefault()
        undo()
        return
      }
      
      // Ctrl+Y: 重做
      if (e.ctrlKey && e.key === 'y') {
        e.preventDefault()
        redo()
        return
      }
      
      // Delete: 删除选中组件
      if (e.key === 'Delete') {
        e.preventDefault()
        deleteSelected()
        return
      }
      
      // Ctrl+G: 分组
      if (e.ctrlKey && e.key === 'g') {
        e.preventDefault()
        if (selectedIds.value.length >= 2) {
          groupComponents(selectedIds.value)
        }
        return
      }
      
      // Ctrl+Shift+G: 取消分组
      if (e.ctrlKey && e.shiftKey && e.key === 'G') {
        e.preventDefault()
        
        // 如果有选中的分组，优先处理分组
        if (selectedGroupIds.value.length > 0) {
          selectedGroupIds.value.forEach(groupId => {
            ungroupComponents(groupId)
          })
          // 清空选中的分组
          selectedGroupIds.value = []
          return
        }
        
        // 否则处理选中的组件
        const groupedComponents = selectedComponents.value.filter(c => c.isGrouped)
        const groupIds = [...new Set(groupedComponents.map(c => c.groupId).filter(Boolean))]
        groupIds.forEach(groupId => ungroupComponents(groupId!))
		currentSelectedGroup.value=undefined;
        return
      }
      
      // 方向键移动（支持单选组件和多选分组）
      if (selectedIds.value.length === 0 && selectedGroupIds.value.length === 0) {
        return
      }
      let step = e.shiftKey ? moveStepFast : moveStep
      if (
        e.key === 'ArrowLeft' ||
        e.key === 'ArrowRight' ||
        e.key === 'ArrowUp' ||
        e.key === 'ArrowDown'
      ) {
        isCanvasInteracting.value = true
        hideHandles.value = true
        
        // 检查是否有分组被选中或有分组组件被选中
        const hasGroupedComponents = selectedIds.value.some(id => {
          const comp = dynamicVisualStore.currentComponents.find(c => c.id === id)
          return comp && comp.isGrouped
        })
        
        const hasSelectedGroups = selectedGroupIds.value.length > 0
        
        if (hasSelectedGroups || hasGroupedComponents) {
          // 移动分组
          if (e.key === 'ArrowLeft') moveSelectedGroup(-step, 0)
          if (e.key === 'ArrowRight') moveSelectedGroup(step, 0)
          if (e.key === 'ArrowUp') moveSelectedGroup(0, -step)
          if (e.key === 'ArrowDown') moveSelectedGroup(0, step)
        } else {
          // 移动单个组件（仅单选时）
          if (selectedIds.value.length === 1) {
            const selectedId = selectedIds.value[0]
            if (e.key === 'ArrowLeft') moveSelectedComponent(-step, 0, selectedId)
            if (e.key === 'ArrowRight') moveSelectedComponent(step, 0, selectedId)
            if (e.key === 'ArrowUp') moveSelectedComponent(0, -step, selectedId)
            if (e.key === 'ArrowDown') moveSelectedComponent(0, step, selectedId)
          }
        }
        e.preventDefault()
      }
      // 层级快捷键
      if (e.ctrlKey && e.key === 'ArrowUp') {
        e.preventDefault()
        moveUp()
        return
      }
      if (e.ctrlKey && e.key === 'ArrowDown') {
        e.preventDefault()
        moveDown()
        return
      }
      if (e.ctrlKey && e.shiftKey && e.key === 'ArrowUp') {
        e.preventDefault()
        bringToFront()
        return
      }
      if (e.ctrlKey && e.shiftKey && e.key === 'ArrowDown') {
        e.preventDefault()
        sendToBack()
        return
      }
    }
    
	function onKeyUp(e: KeyboardEvent) {
		if(dynamicVisualStore.debugMode==true){
			return;
		}
	  const target = e.target as HTMLElement
	  if(target && target.tagName==="INPUT" || target && target.tagName=='TEXTAREA'){
	  	return;
	  }
      
      // 阻止事件冒泡
      e.stopPropagation()
      e.stopImmediatePropagation()
      
      if (
        e.key === 'ArrowLeft' ||
        e.key === 'ArrowRight' ||
        e.key === 'ArrowUp' ||
        e.key === 'ArrowDown'
      ) {
        isCanvasInteracting.value = false
        hideHandles.value = false
      }
    }
    
    // 组拖动相关
    const isGroupDragging = ref(false)
    const groupDragStart = ref({ x: 0, y: 0 })
    const groupOriginPositions = ref<Record<string, { x: number, y: number }>>({})
    const draggingGroupId = ref<string | null>(null)
    
    function onGroupMouseDown(group: ComponentGroup, e: MouseEvent) {
      if (group.locked || dynamicVisualStore.debugMode==true) return; // 锁定时禁止拖动
      e.stopPropagation()
	  
	  if (e.button === 2){
		  return;
	  }
	  
      isGroupDragging.value = true
      draggingGroupId.value = group.id
      
      // 隐藏手柄
      hideHandles.value = true
      
      // 获取画布容器的位置，用于坐标转换
      const designPage = designPageRef.value
      if (!designPage) return
      const rect = designPage.getBoundingClientRect()
      
      // 记录鼠标在原始画布坐标中的位置（考虑画布偏移和缩放）
      groupDragStart.value = { 
        x: (e.clientX - rect.left - canvasOffset.value.x) / canvasScale.value, 
        y: (e.clientY - rect.top - canvasOffset.value.y) / canvasScale.value 
      }
      
      groupOriginPositions.value = {}
      group.componentIds.forEach(id => {
        const comp = dynamicVisualStore.currentComponents.find(c => c.id === id)
        if (comp) groupOriginPositions.value[id] = { x: comp.x, y: comp.y }
      })
      // 记录分组本身的原始位置
      groupOriginPositions.value['__group__'] = { x: group.x, y: group.y }
      window.addEventListener('mousemove', onGroupMouseMove)
      window.addEventListener('mouseup', onGroupMouseUp)
    }
    
    function onGroupMouseMove(e: MouseEvent) {
		if(dynamicVisualStore.debugMode==true){
			return;
		}
		
      if (!isGroupDragging.value || draggingGroupId.value == null) return
      
      // 获取画布容器的位置，用于坐标转换
      const designPage = designPageRef.value
      if (!designPage) return
      const rect = designPage.getBoundingClientRect()
      
      // 计算鼠标在原始画布坐标中的当前位置（考虑画布偏移和缩放）
      const currentX = (e.clientX - rect.left - canvasOffset.value.x) / canvasScale.value
      const currentY = (e.clientY - rect.top - canvasOffset.value.y) / canvasScale.value
    
      // 计算在原始画布坐标中的偏移量
      const dx = currentX - groupDragStart.value.x
      const dy = currentY - groupDragStart.value.y
    
      const group = dynamicVisualStore.currentGroups.find(g => g.id === draggingGroupId.value)
      if (!group) return
    
      // 计算边界（用原始画布坐标）
      const minX = 0
      const minY = 0
      const maxX = pageWidth.value - group.width
      const maxY = pageHeight.value - group.height
    
      // 计算目标位置
      let targetX = groupOriginPositions.value['__group__'].x + dx
      let targetY = groupOriginPositions.value['__group__'].y + dy
    
      // 限制范围
      targetX = Math.max(minX, Math.min(targetX, maxX))
      targetY = Math.max(minY, Math.min(targetY, maxY))
    
      // 实际偏移量
      const realDx = targetX - groupOriginPositions.value['__group__'].x
      const realDy = targetY - groupOriginPositions.value['__group__'].y
    
      // 移动组内组件
      Object.entries(groupOriginPositions.value).forEach(([id, pos]) => {
        if (id === '__group__') return
        const comp = dynamicVisualStore.currentComponents.find(c => c.id === id)
        if (comp) {
          comp.x = pos.x + realDx
          comp.y = pos.y + realDy
        }
      })
      // 移动分组边框
      group.x = targetX
      group.y = targetY
    }
    
    function onGroupMouseUp() {
	  if (dynamicVisualStore.debugMode==true) return; 
      if (!isGroupDragging.value || draggingGroupId.value == null) return
      // 拖动结束后记录历史
      const group = dynamicVisualStore.currentGroups.find(g => g.id === draggingGroupId.value)
      if (group) {
        // 记录组内组件的位置历史
        const componentStates: Record<string, { oldState: { x: number, y: number }, newState: { x: number, y: number } }> = {}
        
        group.componentIds.forEach(id => {
          const comp = dynamicVisualStore.currentComponents.find(c => c.id === id)
          if (comp) {
            const oldState = groupOriginPositions.value[id]
            const newState = { x: comp.x, y: comp.y }
            componentStates[id] = { oldState, newState }
          }
        })
        
        // 记录分组边框的位置历史
        const groupOldState = groupOriginPositions.value['__group__']
        const groupNewState = { x: group.x, y: group.y }
        
        if (groupOldState) {
          addHistory('groupMove', { 
            groupId: group.id, 
            oldState: groupOldState, 
            newState: groupNewState,
            componentStates: componentStates
          })
        }
      }
      isGroupDragging.value = false
      draggingGroupId.value = null
      
      // 还原手柄
      hideHandles.value = false
      
      window.removeEventListener('mousemove', onGroupMouseMove)
      window.removeEventListener('mouseup', onGroupMouseUp)
    }
    
    // 编辑组名相关
    const editingGroupId = ref<string | null>(null)
    const editingGroupName = ref('')
    
    function startEditGroupName(group: ComponentGroup) {
      editingGroupId.value = group.id
      editingGroupName.value = group.name
      nextTick(() => {
        const input = document.getElementById('group-name-input-' + group.id) as HTMLInputElement
        if (input) input.focus()
      })
    }
    
    function finishEditGroupName(group: ComponentGroup) {
      if (editingGroupName.value.trim()) {
        group.name = editingGroupName.value.trim()
      }
      editingGroupId.value = null
}

    
    // 监听 el-slider 拖动，隐藏/显示手柄
    function onSliderPointerDown(e: PointerEvent) {
      const target = e.target as HTMLElement
      if (target && target.classList.contains('el-slider__button')) {
        isCanvasInteracting.value = true
        hideHandles.value = true
        // 只需监听一次 pointerup
        const onPointerUp = () => {
          isCanvasInteracting.value = false
          hideHandles.value = false
          window.removeEventListener('pointerup', onPointerUp, true)
        }
        window.addEventListener('pointerup', onPointerUp, true)
      }
    }
    
    function onSliderFocus() {
      isSliderFocusing.value = true;
    }
    function onSliderBlur() {
      isSliderFocusing.value = false;
    }
    
    // 空格键监听函数
    const handleSpaceKeyDown = (e: KeyboardEvent) => {
	  if(dynamicVisualStore.debugMode==true) return;
      // 检查事件是否来自画布区域或其子元素
	  const target = e.target as HTMLElement
	  if(target && target.tagName==="INPUT" || target && target.tagName=='TEXTAREA'){
	  	return;
	  }
      
      // 阻止事件冒泡
      e.stopPropagation()
      e.stopImmediatePropagation()
      
      if (e.code === 'Space') {
        isSpacePressed.value = true
      }
    }
    
    const handleSpaceKeyUp = (e: KeyboardEvent) => {
	  if(dynamicVisualStore.debugMode==true) return;
	  const target = e.target as HTMLElement
	  if(target && target.tagName==="INPUT" || target && target.tagName=='TEXTAREA'){
	  	return;
	  }
      // 阻止事件冒泡
      e.stopPropagation()
      e.stopImmediatePropagation()
      
      if (e.code === 'Space') {
        isSpacePressed.value = false
      }
    }
    

	
	
	
    
    
    watch([pageWidth, pageHeight], autoFitCanvas)
    
    const canvasOffset = ref({ x: 0, y: 0 })
    const isPanning = ref(false)
    const panStart = ref({ x: 0, y: 0 })
    const mouseStart = ref({ x: 0, y: 0 })
    const isSpacePressed = ref(false)
    
    // 移除重复的空格键监听器
    // window.addEventListener('keydown', (e) => {
    //   if (e.code === 'Space') isSpacePressed.value = true
    // })
    // window.addEventListener('keyup', (e) => {
    //   if (e.code === 'Space') isSpacePressed.value = false
    // })
    
    const onAreaMouseDown = (e: MouseEvent) => {
      // 仅按住空格键或鼠标中键时允许拖动画布
      if (e.button === 1 || isSpacePressed.value) {
        isPanning.value = true
        panStart.value = { ...canvasOffset.value }
        mouseStart.value = { x: e.clientX, y: e.clientY }
        document.body.style.cursor = 'grab'
        window.addEventListener('mousemove', onAreaMouseMove)
        window.addEventListener('mouseup', onAreaMouseUp)
        e.preventDefault()
        isCanvasInteracting.value = true
        //需要隐藏选中组件的手柄
        hideHandles.value = true
      }

      // 左键点击空白区域时取消选中
      if (e.button === 0 && e.target === designAreaRef.value) {
        selectedIds.value = []
        selectedGroupIds.value = []
		currentSelectedGroup.value=undefined;
      }
    }
    const onAreaMouseMove = (e: MouseEvent) => {
      if (!isPanning.value) return
      canvasOffset.value.x = panStart.value.x + (e.clientX - mouseStart.value.x)
      canvasOffset.value.y = panStart.value.y + (e.clientY - mouseStart.value.y)
    }
    const onAreaMouseUp = () => {
      isPanning.value = false
      document.body.style.cursor = ''
      window.removeEventListener('mousemove', onAreaMouseMove)
      window.removeEventListener('mouseup', onAreaMouseUp)
      isCanvasInteracting.value = false
      hideHandles.value = false
    }
    
    function centerCanvas() {
      nextTick(() => {
        const area = designAreaRef.value
        if (!area) return
        const areaW = area.clientWidth
        const areaH = area.clientHeight
        const canvasW = pageWidth.value * canvasScale.value
        const canvasH = pageHeight.value * canvasScale.value
        canvasOffset.value.x = (areaW - canvasW) / 2
        canvasOffset.value.y = (areaH - canvasH) / 2
      })
    }
    
    // MoveableBox 事件处理
    function handleBoxMouseDown(e: MouseEvent, id: string) {
		
	  selectedGroupIds.value=[];	
	  if(e.button==1) return;
		
      // 支持多选：按住 Ctrl 键进行多选
	  if(dynamicVisualStore.debugMode==true) return;
      const multiSelect = e.ctrlKey || e.metaKey
      if(e.button===0){
		selectComponent(id, multiSelect)
	  }

      
    }
    function handleBoxContextMenu(event: MouseEvent, item: ComponentInstance) {
		handleNodeContextMenu(event,{
			id: item.id,
			label: item.title || item.type,
			type: 'component',
			componentId: item.componentId,
			raw: item  
		})
    }
	
	const handleNodeContextMenu=(event: MouseEvent, data: any, _node?: any)=>{
		if(isBoxSelectingTrue.value==false){
			if (data.type=='component' && !selectedIds.value.includes(data.id)) {
				selectComponent(data.id)
			}
			contextMenuRef.value.handleNodeContextMenu(event,data, null)
		}
		isBoxSelectingTrue.value = false;
	}
    
    // 1. 辅助线/吸附功能
    const snapLines = reactive<{ x: number[], y: number[] }>({ x: [], y: [] });
    const SNAP_THRESHOLD = 5;
    const snapEnabled = ref(true); // 辅助线开关
    // 临时拖动状态
    const tempDragState = ref<{ id: string; x: number; y: number; width: number; height: number } | null>(null);
    
    // 切换辅助线开关
    const toggleSnap = () => {
      snapEnabled.value = !snapEnabled.value;
      if (!snapEnabled.value) {
        snapLines.x = [];
        snapLines.y = [];
      }
    };
    
    function getComponentEdges(comp: { x: number; y: number; width: number; height: number }) {
      return {
        left: comp.x,
        right: comp.x + comp.width,
        top: comp.y,
        bottom: comp.y + comp.height,
        centerX: comp.x + comp.width / 2,
        centerY: comp.y + comp.height / 2,
      };
    }
    
    type EdgeKey = 'left' | 'right' | 'centerX' | 'top' | 'bottom' | 'centerY';
    
    function handleSnap(
      id: string,
      nextX: number,
      nextY: number,
      width: number,
      height: number
    ) {
      let snapLinesX: number[] = [], snapLinesY: number[] = [];
      const moving = { x: nextX, y: nextY, width, height };
      const movingEdges = getComponentEdges(moving);
      let snapX = nextX, snapY = nextY;
      let minDx = SNAP_THRESHOLD, minDy = SNAP_THRESHOLD;
      
      // 与其他组件的吸附
      dynamicVisualStore.currentComponents.forEach((comp: any) => {
        if (comp.id === id) return;
        const edges = getComponentEdges(comp);
        
        // 水平吸附：左对齐、右对齐、中心对齐
        (['left', 'right', 'centerX'] as EdgeKey[]).forEach(key => {
          const dx = Math.abs(movingEdges.left - edges[key]);
          if (dx < minDx) {
            minDx = dx;
            snapX = nextX + (edges[key] - movingEdges.left);
            snapLinesX = [edges[key]];
          }
        });
        
        // 垂直吸附：顶部对齐、底部对齐、中心对齐
        (['top', 'bottom', 'centerY'] as EdgeKey[]).forEach(key => {
          const dy = Math.abs(movingEdges.top - edges[key]);
          if (dy < minDy) {
            minDy = dy;
            snapY = nextY + (edges[key] - movingEdges.top);
            snapLinesY = [edges[key]];
          }
        });
        
        // 边缘对齐：移动组件的右边缘与目标组件的左边缘对齐
        const rightToLeft = Math.abs(movingEdges.right - edges.left);
        if (rightToLeft < minDx) {
          minDx = rightToLeft;
          snapX = nextX + (edges.left - movingEdges.right);
          snapLinesX = [edges.left];
        }
        
        // 移动组件的左边缘与目标组件的右边缘对齐
        const leftToRight = Math.abs(movingEdges.left - edges.right);
        if (leftToRight < minDx) {
          minDx = leftToRight;
          snapX = nextX + (edges.right - movingEdges.left);
          snapLinesX = [edges.right];
        }
        
        // 移动组件的底部与目标组件的顶部对齐
        const bottomToTop = Math.abs(movingEdges.bottom - edges.top);
        if (bottomToTop < minDy) {
          minDy = bottomToTop;
          snapY = nextY + (edges.top - movingEdges.bottom);
          snapLinesY = [edges.top];
        }
        
        // 移动组件的顶部与目标组件的底部对齐
        const topToBottom = Math.abs(movingEdges.top - edges.bottom);
        if (topToBottom < minDy) {
          minDy = topToBottom;
          snapY = nextY + (edges.bottom - movingEdges.top);
          snapLinesY = [edges.bottom];
        }
      });
      
      // 画布边界和中心线吸附
      const pageW = pageWidth.value, pageH = pageHeight.value;
      const canvasEdges: Record<EdgeKey, number> = {
        left: 0,
        right: pageW,
        centerX: pageW / 2,
        top: 0,
        bottom: pageH,
        centerY: pageH / 2,
      };
      
      // 水平画布吸附
      (['left', 'right', 'centerX'] as EdgeKey[]).forEach(key => {
        const dx = Math.abs(movingEdges.left - canvasEdges[key]);
        if (dx < minDx) {
          minDx = dx;
          snapX = nextX + (canvasEdges[key] - movingEdges.left);
          snapLinesX = [canvasEdges[key]];
        }
      });
      
      // 垂直画布吸附
      (['top', 'bottom', 'centerY'] as EdgeKey[]).forEach(key => {
        const dy = Math.abs(movingEdges.top - canvasEdges[key]);
        if (dy < minDy) {
          minDy = dy;
          snapY = nextY + (canvasEdges[key] - movingEdges.top);
          snapLinesY = [canvasEdges[key]];
        }
      });
      
      // 画布边缘对齐
      const rightToCanvasLeft = Math.abs(movingEdges.right - canvasEdges.left);
      if (rightToCanvasLeft < minDx) {
        minDx = rightToCanvasLeft;
        snapX = nextX + (canvasEdges.left - movingEdges.right);
        snapLinesX = [canvasEdges.left];
      }
      
      const leftToCanvasRight = Math.abs(movingEdges.left - canvasEdges.right);
      if (leftToCanvasRight < minDx) {
        minDx = leftToCanvasRight;
        snapX = nextX + (canvasEdges.right - movingEdges.left);
        snapLinesX = [canvasEdges.right];
      }
      
      const bottomToCanvasTop = Math.abs(movingEdges.bottom - canvasEdges.top);
      if (bottomToCanvasTop < minDy) {
        minDy = bottomToCanvasTop;
        snapY = nextY + (canvasEdges.top - movingEdges.bottom);
        snapLinesY = [canvasEdges.top];
      }
      
      const topToCanvasBottom = Math.abs(movingEdges.top - canvasEdges.bottom);
      if (topToCanvasBottom < minDy) {
        minDy = topToCanvasBottom;
        snapY = nextY + (canvasEdges.bottom - movingEdges.top);
        snapLinesY = [canvasEdges.bottom];
      }
      
      return { snapX, snapY, snapLinesX, snapLinesY };
    }
    
    // 2. Ctrl+滚轮缩放画布
    function onWheel(e: WheelEvent) {
      if (!e.ctrlKey) return;
      e.preventDefault();
      let nextScale = canvasScale.value;
      if (e.deltaY < 0) {
        nextScale = Math.min(3, +(canvasScale.value + 0.05).toFixed(2));
      } else {
        nextScale = Math.max(0.1, +(canvasScale.value - 0.05).toFixed(2));
      }
      canvasScale.value = nextScale;
    }
	
	
	

	
	
	

    // 脚本部分实现拖动吸附联动
    function clamp(val: number, min: number, max: number): number {
      return Math.max(min, Math.min(val, max));
    }
    
    function onBoxUpdate({ id, x, y, width, height, rotate }: { id: string; x: number; y: number; width: number; height: number; rotate: number }) {
	  if(dynamicVisualStore.debugMode==true) return;
	  
      id = String(id); // 强制 id 为 string
      // 先进行边界限制
      const minX = 0
      const minY = 0
      const maxX = pageWidth.value - width
      const maxY = pageHeight.value - height
      let clampedX = Math.max(minX, Math.min(x, maxX))
      let clampedY = Math.max(minY, Math.min(y, maxY))
      if (snapEnabled.value) {
        const snapResult = handleSnap(id, clampedX, clampedY, width, height)
        const finalX = Math.max(minX, Math.min(snapResult.snapX, maxX))
        const finalY = Math.max(minY, Math.min(snapResult.snapY, maxY))
        snapLines.x = snapResult.snapLinesX
        snapLines.y = snapResult.snapLinesY
        updateComponent(id, { x: finalX, y: finalY, width, height, rotate })
      } else {
        updateComponent(id, { x: clampedX, y: clampedY, width, height, rotate })
      }
    }
	
	const moveableOldState=ref<ComponentInstance>();
	const onBoxDragStart=(_data:any)=>{
		const comp = dynamicVisualStore.currentComponents.find(c => c.id === _data.id)
		if (!comp) {
			console.error('updateComponent: 未找到组件', _data.id, dynamicVisualStore.currentComponents.map(c => c._data.id))
			return;
		}
		moveableOldState.value = { ...comp }
	}
    
    function onBoxDragEnd(_data:any) {
		if(moveableOldState.value && 
			(moveableOldState.value.x!= _data.x || moveableOldState.value.y!= _data.y ||
				moveableOldState.value.width!= _data.width || moveableOldState.value.height!= _data.height ||
				moveableOldState.value.rotate!= _data.rotate
			)
		){
			const comp = dynamicVisualStore.currentComponents.find(c => c.id === _data.id)
			if (!comp) {
				console.error('updateComponent: 未找到组件', _data.id, dynamicVisualStore.currentComponents.map(c => c.id))
				return;
			}
			const oldState = { ...moveableOldState.value }
			let id=oldState.id;
			addHistory('update', { id, oldState, newState: { ...comp } })	
		}
		snapLines.x = [];
		snapLines.y = [];
		moveableOldState.value=undefined;
    }
    

// 全屏
const isFullScreen = ref(false)
const toggleFullScreen = () => {
	const el = document.querySelector('#table-full-screen')
	if (!el) return
	isFullScreen.value = !isFullScreen.value
	el.classList.toggle('el-full-screen')
}

onUnmounted(() => {
    document.removeEventListener('keydown', (e) => {
      if (e.key === 'Escape' && isFullScreen.value) {
        toggleFullScreen()
      }
    })
})

// 锁定相关逻辑
const isSelectedLocked = computed(() => {
	if (selectedIds.value.length === 1) {
	  const comp = dynamicVisualStore.currentComponents.find(c => c.id === selectedIds.value[0])
	  return comp?.locked === true
	}
	return false
})
  
function toggleLockSelected() {
    if (selectedIds.value.length === 1) {
      const comp = dynamicVisualStore.currentComponents.find(c => c.id === selectedIds.value[0])
      if (comp) {
        comp.locked = !comp.locked
      }
    }
}
  
function toggleLockGroup(groupId: string|null) {
    if (!groupId) return;
    const group = dynamicVisualStore.currentGroups.find(g => g.id === groupId);
    if (group) group.locked = !group.locked;
}
  
function toggleVisibleGroup(groupId: string|null) {
    if (!groupId) return;
    const group = dynamicVisualStore.currentGroups.find(g => g.id === groupId);
    if (group) {
		for(var target of group?.componentIds ||  []){
			if(group.visible==true){
				const comp = dynamicVisualStore.currentComponents.find(c => c.id === target)
				if (comp) {
				  comp.visible = false
				}
			}
			else{
				const comp = dynamicVisualStore.currentComponents.find(c => c.id === target)
				if (comp) {
				  comp.visible = true
				}
			}
		}
		group.visible = !group.visible;
	}
}
  
function startEditGroupNameById(groupId: string|null) {
    if (!groupId) return;
    const group = dynamicVisualStore.currentGroups.find(g => g.id === groupId);
    if (group) startEditGroupName(group);
}
  
function handleGroupContextMenu(event: MouseEvent, group: ComponentGroup) {
	handleNodeContextMenu(event,{
		id: group.id,
		label: group.name || group.type,
		type: 'group',
		raw: group  
	})
}

// 分组操作方法
function bringGroupToFront(groupId: string) {
    // 置顶分组（分组在 groups 数组最后，组内组件在 components 最后）
    const groupIdx = dynamicVisualStore.currentGroups.findIndex(g => g.id === groupId)
    if (groupIdx === -1) return
    const group = dynamicVisualStore.currentGroups.splice(groupIdx, 1)[0]
    dynamicVisualStore.currentGroups.push(group)
    // 组内组件也置顶
    const ids = group.componentIds
    const remain = dynamicVisualStore.currentComponents.filter(c => !ids.includes(c.id))
    const selected = dynamicVisualStore.currentComponents.filter(c => ids.includes(c.id))
    dynamicVisualStore.currentComponents = [...remain, ...selected]
}
  function sendGroupToBack(groupId: string) {
    // 置底分组（分组在 groups 数组最前，组内组件在 components 最前）
    const groupIdx = dynamicVisualStore.currentGroups.findIndex(g => g.id === groupId)
    if (groupIdx === -1) return
    const group = dynamicVisualStore.currentGroups.splice(groupIdx, 1)[0]
    dynamicVisualStore.currentGroups.unshift(group)
    // 组内组件也置底
    const ids = group.componentIds
    const remain = dynamicVisualStore.currentComponents.filter(c => !ids.includes(c.id))
    const selected = dynamicVisualStore.currentComponents.filter(c => ids.includes(c.id))
    dynamicVisualStore.currentComponents = [...selected, ...remain]
  }
  function moveGroupUp(groupId: string) {
    // 分组上移一层
    const idx = dynamicVisualStore.currentGroups.findIndex(g => g.id === groupId)
    if (idx === -1 || idx === dynamicVisualStore.currentGroups.length - 1) return
    const temp = dynamicVisualStore.currentGroups[idx]
    dynamicVisualStore.currentGroups[idx] = dynamicVisualStore.currentGroups[idx + 1]
    dynamicVisualStore.currentGroups[idx + 1] = temp
  }
  function moveGroupDown(groupId: string) {
    // 分组下移一层
    const idx = dynamicVisualStore.currentGroups.findIndex(g => g.id === groupId)
    if (idx <= 0) return
    const temp = dynamicVisualStore.currentGroups[idx]
    dynamicVisualStore.currentGroups[idx] = dynamicVisualStore.currentGroups[idx - 1]
    dynamicVisualStore.currentGroups[idx - 1] = temp
  }
  function copyGroup(groupId: string) {
    // 复制分组及其所有组件，位置偏移，ID 唯一
    const group = dynamicVisualStore.currentGroups.find(g => g.id === groupId)
    if (!group) return
    const newGroupId = (Date.now() + Math.floor(Math.random() * 10000)).toString()
    const newComponentIds: string[] = []
    const copiedComponents = group.componentIds.map(cid => {
      const comp = dynamicVisualStore.currentComponents.find(c => c.id === cid)
      if (!comp) return null
      const newId = (Date.now() + Math.floor(Math.random() * 10000) + Math.floor(Math.random() * 10000)).toString()
      newComponentIds.push(newId)
      return {
        ...comp,
        id: newId,
        x: comp.x + 30,
        y: comp.y + 30,
        groupId: newGroupId,
        isGrouped: true,
        title: comp.title ?? '',
        bgColor: comp.bgColor ?? '#ffffff'
      }
    }).filter(Boolean)
    if (copiedComponents.length === 0) return
    dynamicVisualStore.currentComponents.push(...(copiedComponents as any))
    dynamicVisualStore.currentGroups.push({
      ...group,
      id: newGroupId,
      name: group.name + ' (复制)',
      componentIds: newComponentIds,
      x: group.x + 30,
      y: group.y + 30,
      locked: false
    })
  }
  function deleteGroup(groupId: string) {
    // 删除分组及其所有组件
    const groupIdx = dynamicVisualStore.currentGroups.findIndex(g => g.id === groupId)
    if (groupIdx === -1) return
    const group = dynamicVisualStore.currentGroups[groupIdx]
    // 删除组内组件
    dynamicVisualStore.currentComponents = dynamicVisualStore.currentComponents.filter(c => !group.componentIds.includes(c.id))
    // 删除分组
    dynamicVisualStore.currentGroups.splice(groupIdx, 1)
  }

  const isBoxSelecting = ref(false)
  const isRightMouseDown = ref(false) // 新增右键按下状态标志
  const boxSelectStart = ref({ x: 0, y: 0 })
  const boxSelectRect = ref({ x: 0, y: 0, width: 0, height: 0 })
  const isBoxSelectingTrue = ref(false)
  
  function onDesignPageMouseDown(e: MouseEvent) {
    if(dynamicVisualStore.debugMode) return;
    
    // 记录右键按下状态
    if (e.button === 2) {
      isRightMouseDown.value = true
    }
  
    // 右键框选逻辑
    if (e.button === 2 && !isBoxSelecting.value) {
      const rect = designPageRef.value?.getBoundingClientRect()
      if (!rect) return
      
      isBoxSelecting.value = true
      const scale = canvasScale.value
      boxSelectStart.value = { 
        x: (e.clientX - rect.left) / scale, 
        y: (e.clientY - rect.top) / scale 
      }
      boxSelectRect.value = { 
        x: boxSelectStart.value.x, 
        y: boxSelectStart.value.y, 
        width: 0, 
        height: 0 
      }
      
      // 使用 passive: false 确保可以调用 preventDefault
      window.addEventListener('mousemove', onBoxSelectMove, { passive: false })
      window.addEventListener('mouseup', onBoxSelectUp, { once: true }) // 使用 once 确保只触发一次
      e.preventDefault()
      return
    }
  
    // 左键点击空白区域时取消选中
    if (e.button === 0 && e.target === designPageRef.value) {
      selectedIds.value = []
      selectedGroupIds.value = []
      currentSelectedGroup.value = undefined
    }
  }
  
const onBoxSelectMove=(e: MouseEvent)=> {
    // 只有右键按下时才处理移动逻辑
    if (!isRightMouseDown.value) {
      cleanupBoxSelect()
      return
    }
  
    // 确保设计页面引用存在
    const rect = designPageRef.value?.getBoundingClientRect()
    if (!rect) {
      cleanupBoxSelect()
      return
    }
  
    // 防止默认行为（如页面滚动）
    e.preventDefault()
  
    const scale = canvasScale.value
    const curX = (e.clientX - rect.left) / scale
    const curY = (e.clientY - rect.top) / scale
  
    // 只有当移动距离超过阈值时才更新选择框
    if (Math.abs(curX - boxSelectStart.value.x) > 2 || Math.abs(curY - boxSelectStart.value.y) > 2) {
      boxSelectRect.value = {
        x: Math.min(boxSelectStart.value.x, curX),
        y: Math.min(boxSelectStart.value.y, curY),
        width: Math.abs(curX - boxSelectStart.value.x),
        height: Math.abs(curY - boxSelectStart.value.y)
      }
    }
}
  
const onBoxSelectUp=(e: MouseEvent)=> {
    // 只有右键释放时才处理
    if (e.button === 2) {
      isRightMouseDown.value = false
      // 只有当确实在框选时才执行选择逻辑
      if (isBoxSelecting.value) {
        // 检查选择框是否有有效尺寸
        if (boxSelectRect.value.width > 5 && boxSelectRect.value.height > 5) {
			isBoxSelectingTrue.value=true;
			selectInBox()
        }
      }
    }
    cleanupBoxSelect()
}
  
const cleanupBoxSelect=()=> {
	isBoxSelecting.value=false;
	window.removeEventListener('mousemove', onBoxSelectMove)
	// mouseup 事件使用 once: true 自动移除，不需要手动移除
}
  
const selectInBox=()=>{
    const sel = boxSelectRect.value
    // 添加空矩形检查
    if (sel.width <= 0 || sel.height <= 0) return
    
    // 选中组件
    selectedIds.value = dynamicVisualStore.currentComponents
      .filter(c => isRectOverlap(sel, { x: c.x, y: c.y, width: c.width, height: c.height }))
      .map(c => c.id)
    
    // 选中分组
    selectedGroupIds.value = dynamicVisualStore.currentGroups
      .filter(g => isRectOverlap(sel, { x: g.x, y: g.y, width: g.width, height: g.height }))
      .map(g => g.id)
}
  
const isRectOverlap=(a: {x:number,y:number,width:number,height:number}, b: {x:number,y:number,width:number,height:number})=> {
    return a.x < b.x + b.width && a.x + a.width > b.x &&
		a.y < b.y + b.height && a.y + a.height > b.y
}

const hasUnsavedChanges=()=> {
    // 判断是否有未保存内容（可根据实际业务调整）
    return history.value.length > 0
}


//切换页面时同步当前页面数据
const switchPage=(index: number)=> {
    if (!dynamicVisualStore.pages[index]) return;
    dynamicVisualStore.currentPageIndex = index;
    const page = dynamicVisualStore.pages[index];
    pageTitle.value = page.title;
    pageSize.value = page.pageSize;
    pageBgColor.value = page.pageBgColor;
    pageBgImg.value = page.pageBgImg;
    fitMode.value = page.fitMode;
	pageBindings.value= page.bindings;
    Object.assign(filter.value, page.filter);
    themeColor.value = page.themeColor;
    // 修复响应式：用新数组赋值
    dynamicVisualStore.currentComponents = page.components ? [...page.components] : [];
    dynamicVisualStore.currentGroups = page.groups ? [...page.groups] : [];
    history.value = page.history;
    historyIndex.value = page.historyIndex;
	
	currentSelectedGroup.value=undefined;
}

// 新建页面
const createNewPage=async ()=> {
    if (hasUnsavedChanges()) {
      try {
        await ElMessageBox.confirm('当前页面有未保存内容，是否保存后新建？', '提示', {
          confirmButtonText: '保存并新建',
          cancelButtonText: '取消',
          distinguishCancelAndClose: true,
          type: 'warning',
          closeOnClickModal: false,
          showCancelButton: true,
          showClose: true,
          showConfirmButton: true,
          confirmButtonClass: 'el-button--primary'
        })
        saveProject()
        doCreateNewPage()
      } catch (e) {
        // 用户取消
      }
    } else {
      doCreateNewPage()
    }
}
  
const doCreateNewPage=()=> {
    // 保存当前页面状态
    dynamicVisualStore.pages[dynamicVisualStore.currentPageIndex] = {
      ...dynamicVisualStore.pages[dynamicVisualStore.currentPageIndex],
      title: pageTitle.value,
      pageSize: pageSize.value,
      pageBgColor: pageBgColor.value,
      pageBgImg: pageBgImg.value,
      fitMode: fitMode.value,
      filter: filter.value,
      themeColor: themeColor.value,
      components: dynamicVisualStore.currentComponents,
      groups: dynamicVisualStore.currentGroups,
      history: history.value,
      historyIndex: historyIndex.value,
	  bindings:pageBindings.value
    }
    // 新建页面对象
    const newPage: PageData = {
      id: Date.now(),
      title: '新页面',
      pageSize: '1920x1080',
      pageBgColor: '#1a2b4c',
      pageBgImg: '',
      fitMode: 'contain',
      filter: { hue: 0, saturate: 100, contrast: 100, brightness: 100, opacity: 100 },
      themeColor: '#409EFF',
      components: [],
      groups: [],
      history: [],
      historyIndex: -1,
	  bindings:[]
    }
    dynamicVisualStore.pages.push(newPage)
    dynamicVisualStore.currentPageIndex =dynamicVisualStore.pages.length - 1
    // 切换到新页面
    pageTitle.value = newPage.title
    pageSize.value = newPage.pageSize
    pageBgColor.value = newPage.pageBgColor
    pageBgImg.value = newPage.pageBgImg
    fitMode.value = newPage.fitMode
	pageBindings.value=newPage.bindings
    Object.assign(filter.value, newPage.filter)
    themeColor.value = newPage.themeColor
    dynamicVisualStore.currentComponents = []
    dynamicVisualStore.currentGroups = []
    history.value = []
    historyIndex.value = -1
    ElMessage.success('已创建新页面')
}

const pageStyle = computed(() =>
    getPageStyle({
		pageBgColor: pageBgColor.value,
		pageBgImg: pageBgImg.value,
		fitMode: fitMode.value,
		filter: filter.value,
		width: pageWidth.value * canvasScale.value,
		height: pageHeight.value * canvasScale.value
    })
)

const handleStructureContextMenu=({ command, node }: { command: string, node: any })=> {
    if (node.type === 'group') {
      const groupId = node.id.replace('group-', '')
      if (command === 'rename') {
        startEditGroupNameById(groupId)
      } else if (command === 'lock') {
        toggleLockGroup(groupId)
      } else if (command === 'ungroup') {
        ungroupComponents(groupId)
      } else if (command === 'delete') {
        deleteGroup(groupId)
      }
	  else if (command === 'visible') {
	    toggleVisibleGroup(groupId)
	  }
	  else if (command === 'copy') {
		  copyGroup(groupId)
	  }
	  else if (command === 'front') {
		  bringGroupToFront(groupId)
	  }
	  else if (command === 'back') {
		  sendGroupToBack(groupId)
	  }
	  else if (command === 'up') {
		  moveGroupUp(groupId)
	  }
	  else if (command === 'down') {
		  moveGroupDown(groupId)
	  }
    } else if (node.type === 'component') {
      const compId = node.raw.id
      if (command === 'lock') {
        const comp = dynamicVisualStore.currentComponents.find(c => c.id === compId)
        if (comp) comp.locked = !comp.locked
      } else if (command === 'delete') {
        removeComponent(compId)
      }
	  else if (command === 'visible') {
	    toggleVisible()
	  }
	  else if (command === 'selectAll') {
		  selectAll()
	  }
	  else if (command === 'down') {
		  moveDown()
	  }
	  else if (command === 'up') {
		  moveUp()
	  }
	  else if (command === 'back') {
		  sendToBack()
	  }
	  else if (command === 'front') {
		  bringToFront()
	  }
	  else if (command === 'copy') {
		  copySelected()
	  }
    }
}

onMounted(async () => {
	
	document.addEventListener('pointerdown', onSliderPointerDown, true)
	window.addEventListener('keydown', onKeyDown, true)
	window.addEventListener('keyup', onKeyUp, true)
	
	// 空格键监听 - 只针对画布区域
	window.addEventListener('keydown', handleSpaceKeyDown, true)
	window.addEventListener('keyup', handleSpaceKeyUp, true)
	dynamicVisualStore.currentComponents = []
	const area = designAreaRef.value;
	if (area) area.addEventListener('wheel', onWheel, { passive: false });
	
	//监听ESC键退出全屏
	// document.addEventListener('keydown', (e) => {
	//   if (e.key === 'Escape' && isFullScreen.value) {
	//     toggleFullScreen()
	//   }
	// })
	toggleFullScreen();
	centerCanvas()
    const projectId = typeof route.query.id === 'string' ? route.query.id : '';
    if (!projectId) return;
    try {
      const data = await ProjectService.getPagesByProjectId(projectId);
      console.log('data=',data);
      if (data && data.pages && Array.isArray(data.pages) && data.pages.length > 0) {
        // id、groupId、componentIds 全部 string 化
        data.pages = data.pages.map((page: any) => ({
          ...page,
          id: String(page.id),
          components: page.components ? page.components.map((c: any) => ({ ...c, id: String(c.id), groupId: c.groupId ? String(c.groupId) : undefined })) : [],
          groups: page.groups ? page.groups.map((g: any) => ({ ...g, id: String(g.id), componentIds: g.componentIds.map((cid: any) => String(cid)) })) : [],
        }));
       
       dynamicVisualStore.pages = data.pages;
        switchPage(0);
      }
      if(data && data.project){
        appTitle.value=data.project.name
      }
    } catch (e) {
      console.error('加载项目数据失败', e);
      ElMessage.error('加载项目数据失败');
    }
});


onBeforeUnmount(() => {
	const area = designAreaRef.value;
	if (area) area.removeEventListener('wheel', onWheel);

	document.removeEventListener('pointerdown', onSliderPointerDown, true)
	window.removeEventListener('keydown', onKeyDown, true)
	window.removeEventListener('keyup', onKeyUp, true)

	// 移除空格键监听
	window.removeEventListener('keydown', handleSpaceKeyDown, true)
	window.removeEventListener('keyup', handleSpaceKeyUp, true)

	// 清空组件数据模型缓存
	const componentDataModelStore = useComponentDataModelStore();
	componentDataModelStore.clearCache();
	dynamicComponentStore.clearRefs()
	componentManager.clearCache()
});

const backProject = async () => {
    if (hasUnsavedChanges()) {
      try {
        await ElMessageBox.confirm(
          '当前页面有未保存内容，是否保存后返回？',
          '提示',
          {
            confirmButtonText: '保存并返回',
            cancelButtonText: '直接返回',
            distinguishCancelAndClose: true,
            type: 'warning',
            closeOnClickModal: false,
            showCancelButton: true,
            showClose: true,
            showConfirmButton: true,
            confirmButtonClass: 'el-button--primary'
          }
        )
        await saveProject()
        router.push('/visual-app/project')
      } catch (e) {
        // 用户点击"直接返回"或关闭弹窗
        if (e === 'cancel' || e === 'close') {
          router.push('/visual-app/project')
        }
      }
    } else {
      router.push('/visual-app/project')
    }
}

const removePage=(idx: number)=> {
    dynamicVisualStore.pages.splice(idx, 1);
    if (dynamicVisualStore.pages.length === 0) {
      dynamicVisualStore.currentPageIndex = -1;
      pageTitle.value = '';
      pageSize.value = '1920x1080';
      pageBgColor.value = '#1a2b4c';
      pageBgImg.value = '';
      fitMode.value = 'contain';
      Object.assign(filter.value, { hue: 0, saturate: 100, contrast: 100, brightness: 100, opacity: 100 });
      themeColor.value = '#409EFF';
      dynamicVisualStore.currentComponents = [];
      dynamicVisualStore.currentGroups = [];
      history.value = [];
      historyIndex.value = -1;
	  pageBindings.value=[];
      ElMessage.success('页面已全部删除');
      return;
    }
    if (dynamicVisualStore.currentPageIndex >= dynamicVisualStore.pages.length) {
      dynamicVisualStore.currentPageIndex = dynamicVisualStore.pages.length - 1;
    }
    switchPage(dynamicVisualStore.currentPageIndex);
    ElMessage.success('页面已删除');
}

// 画布区域拖拽开始事件
const onDesignAreaDragStart = (event: DragEvent) => {
	event.preventDefault()
}

defineExpose({
	dropComponent,
	selectComponent,
	onAddComponentFromToolbar,
	handleGroupContextMenu
})

</script>

<style lang="scss" scoped>
.custom-pane {
	display: flex;
	flex-direction: column;
	height: 100vh;
	border: 1px solid var(--art-card-border);
	padding: 10px 10px 0;
	background-color: var(--art-main-bg-color);
	border-radius: calc(var(--custom-radius) / 2 + 2px);
  
	.head {
        height: 4rem;
		min-height: 4rem;
		border-bottom: 1px solid var(--art-card-border);
		display: flex;
		justify-content: flex-start;
		align-items: center;
	}	
  
	.body {
		flex: 1;
        display: flex;
        min-height: 0;
		overflow: hidden;
	}
}

.components-panel {
	width: 260px;
	background: var(--panel-bg);
	border-right: 1px solid var(--art-card-border);
	border-top: 0px;
	padding: 0;
	overflow: hidden;
  
	h3 {
		margin: 10px 0 15px;
		color: #a0b1e0;
		font-size: 16px;
		display: flex;
		align-items: center;
		i {
			margin-right: 8px;
		}
	}
}

.components-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}

.component-item {
  background: rgba(30, 60, 128, 0.5);
  border: 1px solid var(--border-color);
  border-radius: 4px;
  padding: 12px;
  cursor: grab;
  text-align: center;
  transition: all 0.3s;
  display: flex;
  flex-direction: column;
  align-items: center;
  
  &:hover {
    background: rgba(60, 100, 200, 0.4);
    transform: translateY(-3px);
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
  }
}

.component-icon {
  font-size: 28px;
  margin-bottom: 8px;
}

.component-name {
  font-size: 13px;
}

.design-area {
	flex: 1;
	min-width: 0;
	min-height: 0;
	height: 100%;
	width: 100%;
	position: relative;
	overflow: hidden;
  background-image: radial-gradient(#ada1a17e 1px, transparent 1px);
  background-size: 15px 15px;
  background-position: 0 0;
	margin-bottom: 0;
  .grid-container {
    position: relative;
    width: 100%;
    height: 100%;
    min-height: 600px;
    background: rgba(20, 40, 80, 0.3);
    border-radius: 4px;
    border: 1px dashed rgba(100, 150, 255, 0.3);
  }
  
  .grid-item {
    position: absolute;
    background: white;
    border-radius: 4px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    overflow: hidden;
    cursor: move;
    transition: all 0.2s;
    
    &:hover {
      box-shadow: 0 2px 20px 0 rgba(100, 150, 255, 0.5);
    }
    
    &.selected {
      border: 2px solid #409EFF;
      box-shadow: 0 0 15px rgba(64, 158, 255, 0.6);
      z-index: 10;
    }
  }
  
  .component-content {
    width: 100%;
    height: 100%;
    position: relative;
        
        .group-indicator {
          position: absolute;
          top: 4px;
          right: 4px;
          color: #409EFF;
          font-size: 12px;
          opacity: 0.8;
        }
  }
  
  .component-preview {
    width: 100%;
    height: calc(100% - 30px);
    padding: 10px;
    box-sizing: border-box;
    
    .bar-chart {
      display: flex;
      height: 80%;
      align-items: flex-end;
      justify-content: space-around;
      padding: 0 10px;
      
      .bar {
        width: 20%;
        border-radius: 3px 3px 0 0;
      }
    }
    
    .line-chart {
      height: 80%;
      position: relative;
      
      .line {
        position: absolute;
        width: 100%;
        height: 100%;
        background: 
          linear-gradient(to top, rgba(100, 150, 255, 0.3) 0.5px, transparent 0.5px),
          linear-gradient(to right, rgba(100, 150, 255, 0.3) 0.5px, transparent 0.5px);
        background-size: 20% 25%;
        
        &::before {
          content: "";
          position: absolute;
          bottom: 0;
          left: 0;
          width: 100%;
          height: 100%;
          background: 
            linear-gradient(to top, transparent 30%, #409EFF 30%, #409EFF 50%, transparent 50%),
            linear-gradient(to top, transparent 40%, #67C23A 40%, #67C23A 70%, transparent 70%),
            linear-gradient(to top, transparent 70%, #E6A23C 70%, #E6A23C 40%, transparent 40%),
            linear-gradient(to top, transparent 60%, #F56C6C 60%, #F56C6C 80%, transparent 80%);
          background-size: 25% 100%;
          background-repeat: no-repeat;
          background-position: 0% 0%, 25% 0%, 50% 0%, 75% 0%;
          clip-path: polygon(0% calc(100% - var(--points, 0%)), 
                       20% calc(100% - var(--points, 0%)), 
                       40% calc(100% - var(--points, 0%)), 
                       60% calc(100% - var(--points, 0%)), 
                       80% calc(100% - var(--points, 0%)), 
                       100% calc(100% - var(--points, 0%)));
        }
      }
    }
    
    .pie-chart {
      width: 80px;
      height: 80px;
      border-radius: 50%;
      background: conic-gradient(
        #409EFF 0% 30%, 
        #67C23A 30% 60%, 
        #E6A23C 60% 85%, 
        #F56C6C 85% 100%
      );
      margin: 10px auto;
    }
    
    .gauge {
      text-align: center;
      padding-top: 10px;
      
      .gauge-circle {
        width: 80px;
        height: 40px;
        background: #f0f0f0;
        border-radius: 80px 80px 0 0;
        margin: 0 auto;
        position: relative;
        overflow: hidden;
        
        &::before {
          content: "";
          position: absolute;
          width: 100%;
          height: 100%;
          background: conic-gradient(#67C23A 0% 70%, #E6A23C 70% 85%, #F56C6C 85% 100%);
          border-radius: inherit;
          transform: rotate(180deg);
          transform-origin: bottom center;
          clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
        }
      }
      
      .gauge-pointer {
        position: absolute;
        bottom: -5px;
        left: 50%;
        width: 2px;
        height: 35px;
        background: #333;
        transform: translateX(-50%) rotate(45deg);
        transform-origin: bottom center;
        z-index: 2;
      }
      
      .gauge-value {
        font-size: 18px;
        font-weight: bold;
        margin-top: 5px;
      }
    }
    
    .stat-card {
      text-align: center;
      padding: 15px;
      
      .stat-title {
        font-size: 14px;
        color: #666;
      }
      
      .stat-value {
        font-size: 32px;
        font-weight: bold;
        margin: 10px 0;
        color: #409EFF;
      }
      
      .stat-trend {
        font-size: 12px;
        color: #67C23A;
      }
    }
    
    .data-table {
      width: 100%;
      height: 100%;
      font-size: 12px;
      
      .table-row {
        display: flex;
        border-bottom: 1px solid #eee;
        
        &.header {
          font-weight: bold;
          background: #f5f7fa;
        }
      }
      
      .table-cell {
        flex: 1;
        padding: 5px 8px;
        
        .status {
          padding: 2px 6px;
          border-radius: 3px;
          font-size: 10px;
          
          &.normal {
            background: #e1f3d8;
            color: #67C23A;
          }
          
          &.warning {
            background: #faecd8;
            color: #E6A23C;
          }
        }
      }
    }
    
    .progress-ring {
      text-align: center;
      padding-top: 15px;
      
      .ring {
        width: 70px;
        height: 70px;
        border-radius: 50%;
        background: conic-gradient(#409EFF 0% var(--progress), #eee var(--progress) 100%);
        margin: 0 auto;
        position: relative;
        
        &::before {
          content: "";
          position: absolute;
          width: 50px;
          height: 50px;
          background: white;
          border-radius: 50%;
          top: 10px;
          left: 10px;
        }
      }
      
      .progress-value {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        font-size: 16px;
        font-weight: bold;
      }
    }
  }
  
  .component-title {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    background: rgba(0, 0, 0, 0.6);
    color: white;
    padding: 5px;
    font-size: 12px;
    text-align: center;
  }
  
  .drag-handle {
    position: absolute;
    top: 5px;
    left: 5px;
    padding: 3px;
    cursor: move;
    opacity: 0.3;
    transition: opacity 0.3s;
    color: #666;
    
    &:hover {
      opacity: 1;
    }
  }
  
  .component-actions {
    position: absolute;
    top: 5px;
    right: 5px;
    opacity: 0;
    transition: opacity 0.3s;
  }
  
  .grid-item:hover .component-actions {
    opacity: 1;
  }
  
  .empty-design-area {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
    color: #a0b1e0;
    
    i {
      font-size: 48px;
      margin-bottom: 15px;
      opacity: 0.5;
    }
    
    p {
      font-size: 16px;
      margin: 0;
    }
  }
}

.properties-panel {
  width: 360px;
  background: var(--panel-bg);
  border-left: 1px solid var(--art-card-border);
  padding: 15px;
  overflow-y: hidden;
  position: relative;
  .panel-section {
    margin-bottom: 20px;
    padding-bottom: 15px;
    border-bottom: 1px solid var(--art-card-border);
  }
  
  .panel-title {
    display: flex;
    align-items: center;
    margin-bottom: 15px;
    color: #a0b1e0;
    
    i {
      margin-right: 8px;
    }
  }
  
  .no-component-selected, .property-form {
    background: rgba(30, 60, 128, 0.1);
    border-radius: 4px;
    padding: 20px;
    text-align: center;
    color: #a0b1e0;
    
    i {
      font-size: 36px;
      margin-bottom: 10px;
      opacity: 0.7;
    }
  }
  
  .property-form {
    text-align: left;
  }
}
    
    // 调整大小手柄样式
    .resize-handle {
      position: absolute;
      background: #409EFF;
      z-index: 10;
    }
    
    .resize-right {
      right: 0;
      top: 0;
      height: 100%;
      width: 5px;
      cursor: e-resize;
    }
    
    .resize-bottom {
      bottom: 0;
      left: 0;
      width: 100%;
      height: 5px;
      cursor: s-resize;
    }
    
    .resize-corner {
      right: 0;
      bottom: 0;
      width: 10px;
      height: 10px;
      background: #409EFF;
      cursor: se-resize;
    }
    
	.design-page-outer {
	  box-shadow: 0 0 0 1px #2a3b5c, 0 4px 32px 0 rgba(0,0,0,0.12);
	  // border-radius: 8px;
	  overflow: visible;
	  min-width: 100px;
	  min-height: 100px;
	  transition: width 0.2s, height 0.2s;
	}

    
    // 新增画布区域样式
    .design-page {
      min-width: 100%;
      min-height: 100%;
      display: block;
      background: transparent;
      transform-origin: left top !important;
    }
    .canvas-area {
      position: relative;
      box-shadow: 0 0 0 1px #2a3b5c, 0 4px 32px 0 rgba(0,0,0,0.12);
      border-radius: 8px;
      overflow: visible;
      min-width: 600px;
      min-height: 400px;
      transition: transform 0.2s;
    }
    
    .zoom-toolbar {
      position: absolute;
      right: 24px;
      bottom: 24px;
      z-index: 20;
      display: flex;
      align-items: center;
	  background-color: var(--art-main-bg-color);
	  color: var(--art-text-gray-700);
      border-radius: 20px;
      box-shadow: 0 2px 8px rgba(0,0,0,0.12);
      padding: 6px 10px;
      user-select: none;
	  margin-bottom: 6px;
      .el-button {
        margin: 0 2px;
      }
      .zoom-label {
        color: #fff;
        font-size: 14px;
        margin-left: 8px;
        min-width: 38px;
        text-align: right;
        font-variant-numeric: tabular-nums;
      }
    }
    
    // 多选状态样式
    .grid-item.selected {
      border: 2px solid #409EFF;
      box-shadow: 0 0 15px rgba(64, 158, 255, 0.6);
      z-index: 10;
    }
    
    .grid-item.multi-selected {
      border: 2px solid #67C23A;
      box-shadow: 0 0 15px rgba(103, 194, 58, 0.6);
      z-index: 10;
    }
    
    // 工具栏按钮组样式
    .el-button-group {
      .el-button {
        border-radius: 4px;
        
        &:not(:last-child) {
          border-right: 1px solid #dcdfe6;
        }
      }
    }
    
    // 分组容器样式
    .group-container {
      .group-label {
        white-space: nowrap;
        font-weight: 500;
      }
      
      &.group-selected {
        .group-label {
          background: #E6A23C !important;
          color: white;
        }
      }
    }
    
    // 状态栏样式
    .status-bar {
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      height: 24px;
      //background: rgba(30, 60, 128, 0.9);
	  background-color: var(--art-main-bg-color);
      color:var(--art-text-gray-700);
      font-size: 12px;
      display: flex;
      align-items: center;
      padding: 0 12px;
      border-top: 1px solid var(--border-color);
      
      .status-item {
        margin-right: 16px;
        
        &:last-child {
          margin-right: 0;
        }
      }
    }
    
    // 辅助线样式
    .snap-line-x, .snap-line-y {
      position: absolute;
      background: #409eff;
      z-index: 9998;
      pointer-events: none;
      opacity: 0.8;
    }
    
    .snap-line-x {
      width: 1px;
      height: 100%;
    }
    
    .snap-line-y {
      height: 1px;
      width: 100%;
    }
    
    // 吸附点样式
    .snap-dot {
      position: absolute;
      width: 6px;
      height: 6px;
      background: #409eff;
      border-radius: 50%;
      z-index: 9999;
      pointer-events: none;
      box-shadow: 0 0 4px rgba(64, 158, 255, 0.8);
    }
    
    // 组件选中状态样式
    .component-content {
      &.selected {
        border-color: #409EFF !important;
		box-shadow: rgb(64, 158, 255) 0px 0px 20px;
      }
      
      .default-component-display {
        width: 100%;
        height: 100%;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 10px;
        box-sizing: border-box;
        
        span {
          font-size: 14px;
          font-weight: 500;
          color: #333;
          margin-bottom: 8px;
        }
        
        .group-indicator {
          position: absolute;
          top: 4px;
          right: 4px;
          color: #409EFF;
          font-size: 12px;
          opacity: 0.8;
        }
  }
}

    .btn {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 32px;
        height: 32px;
        margin-left: 10px;
        color: var(--art-gray-700);
        cursor: pointer;
        background-color: rgba(var(--art-gray-200-rgb), 0.8);
        border-radius: 6px;
        transition: color 0.3s;
        transition: all 0.3s;

        i {
          font-size: 16px;
          color: var(--art-gray-700);
          user-select: none;
        }

        &:hover {
          background-color: rgba(var(--art-gray-300-rgb), 0.75);

          i {
            color: var(--art-gray-800);
          }
        }
    }

    .btn-group{
      display: flex;
      justify-content: space-around;
      align-items: center;
      height: 32px;
	  margin-left: 10px;
      >.b {
        display: flex;
        align-items: center;
        justify-content: center;
        height:  32px;
		min-width: 2rem;
        color: var(--art-gray-700);
        cursor: pointer;
        background-color: rgba(var(--art-gray-200-rgb), 0.8);
        transition: color 0.3s;
        transition: all 0.3s;
        i {
          font-size: 16px;
          color: var(--art-gray-700);
          user-select: none;
        }
		
		span{
			font-size: 16px;
			color: var(--art-gray-700);
			user-select: none;
		}

        &:hover {
          background-color: rgba(var(--art-gray-300-rgb), 0.75);

          i {
            color: var(--art-gray-800);
          }
		  
		  span {
		    color: var(--art-gray-800);
		  }
        }

		
      }
      >.b:first-child{
        border-radius: 6px 0px 0px 6px;
        border-right:1px solid var(--art-card-border);

      }
      >.b:last-child{
        border-radius: 0px 6px 6px 0px;
      }
    }

    .box-select-rect {
      position: absolute;
      border: 1.5px dashed #409EFF;
      background: rgba(64,158,255,0.08);
      pointer-events: none;
      z-index: 9999;
    }

.page-title-bar {
  display: flex;
  align-items: center;
  font-size: 1.15em;
  .page-switch-wrap {
    display: inline-flex;
    align-items: center;
    margin-left: 20px;
    // background: rgba(64,158,255,0.06);
    border-radius: 6px;
    padding: 2px 10px 2px 6px;
    .el-icon-document {
      color: #409EFF;
      font-size: 18px;
      margin-right: 4px;
    }
    .page-switch-label {
      color: #409EFF;
      font-size: 15px;
      margin-right: 6px;
    }
    .page-switch-select {
      width: 240px;
      font-size: 15px;
      .el-input__inner {
        border-radius: 4px;
        background: #fff;
      }
    }
  }
}
::v-deep(.page-switch-select .el-select-dropdown__item.selected) {
  background: #ecf5ff !important;
  color: #409EFF !important;
}
::v-deep(.page-switch-select .el-select-dropdown__item:hover) {
  background: #f0faff !important;
}

</style>